diff --git a/kompare/AUTHORS b/kompare/AUTHORS new file mode 100644 index 00000000..72381951 --- /dev/null +++ b/kompare/AUTHORS @@ -0,0 +1,2 @@ +Otto Bruggeman +John Firebaugh diff --git a/kompare/CMakeLists.txt b/kompare/CMakeLists.txt new file mode 100644 index 00000000..86e4504a --- /dev/null +++ b/kompare/CMakeLists.txt @@ -0,0 +1,48 @@ +cmake_minimum_required(VERSION 2.6) +project(kompare) + +# search packages used by KDE +find_package(KDE4 REQUIRED) +include(KDE4Defaults) +add_definitions (${QT_DEFINITIONS} ${KDE4_DEFINITIONS}) + +find_package(LibKompareDiff2 REQUIRED) +include_directories (${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} ${KDE4_INCLUDES} ${LIBKOMPAREDIFF2_INCLUDE_DIR}) + +add_subdirectory( doc ) +add_subdirectory( interfaces ) +add_subdirectory( libdialogpages ) +add_subdirectory( komparenavtreepart ) +add_subdirectory( komparepart ) +add_subdirectory( pics ) + +include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/komparepart + ${CMAKE_CURRENT_SOURCE_DIR}/libdialogpages + ${CMAKE_CURRENT_SOURCE_DIR}/interfaces + ${CMAKE_CURRENT_SOURCE_DIR}/komparenavtreepart + ${CMAKE_CURRENT_SOURCE_DIR}/komparepart) + + +########### next target ############### + +set(kompare_SRCS +main.cpp +kompare_shell.cpp +kompareurldialog.cpp +) + +kde4_add_app_icon(kompare_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/pics/hi*-app-kompare.png") + +kde4_add_executable(kompare ${kompare_SRCS}) + +target_link_libraries(kompare kompareinterface ${LIBKOMPAREDIFF2_LIBRARIES} komparedialogpages ${KDE4_KTEXTEDITOR_LIBS} ) + +install(TARGETS kompare ${INSTALL_TARGETS_DEFAULT_ARGS} ) + + +########### install files ############### + +install( PROGRAMS kompare.desktop DESTINATION ${XDG_APPS_INSTALL_DIR} ) +install( FILES kompareui.rc DESTINATION ${DATA_INSTALL_DIR}/kompare ) +install( FILES komparenavigationpart.desktop kompareviewpart.desktop DESTINATION ${SERVICETYPES_INSTALL_DIR} ) + diff --git a/kompare/COPYING b/kompare/COPYING new file mode 100644 index 00000000..37ba36f1 --- /dev/null +++ b/kompare/COPYING @@ -0,0 +1,340 @@ + 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/kompare/COPYING.DOC b/kompare/COPYING.DOC new file mode 100644 index 00000000..a988da5a --- /dev/null +++ b/kompare/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/kompare/ChangeLog b/kompare/ChangeLog new file mode 100644 index 00000000..ad8259db --- /dev/null +++ b/kompare/ChangeLog @@ -0,0 +1,423 @@ +Dec 27, 2004 : Jeff Snyder + * Fix for bug 95640 (nothing displayed when kompare is embedded in Ark + fixed by forcing the delivery of childEvents to komparesplitter at + the end of its constructor + +Dec 20, 2004 : Jeff Snyder + * Things that have happened since 3.3: + (this list is not complete) + * Look & feel changed + +Dec 20, 2004 : Jeff Snyder + * Things that were changed sometime between Nov 25, 2003 and KDE 3.3: + (this list is not complete) + * KompareConnectWidget became draggable, by replacing KompareViewFrame + with KompareSplitter. + +Dec 20, 2004 : Jeff Snyder + * This changelog seems to have been neglected for over a year now. I'll + try to retroactively fix this as and when I remember things that have + been fixed - but it'll probably never be complete and accurate for the + Dec 2003 - Nov 2004 period. I'll be making entries concering what i'm + doing with kompare from now onwards. + +Nov 25, 2003 : Otto Bruggeman + * Fix nasty looping to the end of the file when hitting previous difference on the first difference in the first file + +Nov 25, 2003 : Laurent Montel + * Fix memleak, QStringList is implicitly shared so no need for a reference, it is already a pointer to data thing + +Nov 23, 2003 : Otto Bruggeman + * Fixed version string (bug 68872) + * Fix for 68871 (added slotNextDifference to slotApplyDifference()) + * Fix for a crash: dont call blendOriginalIntoModelList with Kompare::ShowingDiff + +Nov 22, 2003 : Otto Bruggeman + * Fix to make the bugs.kde.org dialog pop up instead of sending a mail to John when pressing + Help->Report bug... Also added my homepage since it has always been kompare's home imo. + +Nov 22, 2003 : Otto Bruggeman + * Rework the blendFile method so it actually works and as a bonus is a bit faster + This introduces a new form of show entire file when comparing, one that works + And because of it, it saves files properly now because the entire file is now available even if + you have a single line change in a million line file with only 2 context lines in the diff. + +Nov 22, 2003 : Otto Bruggeman + * Remove the Show entire file option. It only causes problems at the moment + Fixes bug 68729 + +Nov 22, 2003 : Otto Bruggeman + * Commenting out a lot of debug output, it has served it's purpose well in levenshteintable.cpp + +Nov 21, 2003 : Otto Bruggeman + * Also expand tabs to spaces in strings without or after Commands (in the INLINE_DIFFERENCES + code path and yes commands is a shitty name for them but i cant think of something decent) + +Nov 21, 2003 : Otto Bruggeman + * Real Fix (tm) for activating the Swap source with destination action + +Nov 21, 2003 : Otto Bruggeman + * Fix empty line drawing in the INLINE_DIFFERENCES code path + +Nov 20, 2003 : Otto Bruggeman + * When swapping source with destination also change the windows caption and the statusbar text + * Make sure that when swapping and when there are changes, all changes that were made can be + saved, discarded or cancel the whole swap (strings are recycled from the queryClose method) + * Give a better parent to the KIO::NetAccess::download in komparemodellist.cpp + * Added some FIXME's for after the branching to make the urls appear in bold in the error message + * Make queryClose not use the isModified from the part but from the modellist + +Nov 20, 2003 : Otto Bruggeman + * Fix for activating the Swap Source with Destination action. + +Nov 19, 2003 : Otto Bruggeman + * Fixed bug 68570, it needed temp vars otherwise it was overwriting source with destination and then + overwriting that destination with source which was just changed into destination + +Nov 17, 2003 : Otto Bruggeman + * Fix for empty -x and -X arguments. + * Fix bugs 58858 and 58531 by using Kompare::Custom instead of Kompare::Default + * Fix last selected url in the kurlcomboboxes + * Fix for inline differences when there is only 1 char left that still needs to be drawn + * Remove support for the -a Treat all files as text diff option. This caused all sorts of weird crashes + when parsing the diff output now with the custom options. + * Move the per preference page code in the diffprefs constructor into seperate methods per page + +Nov 14, 2003 : Otto Bruggeman + * Fix to make Kompare listen to the kdisplayFontChanged signal and set the font properly and redraw with the new font. + Found by David Faure. + +Nov 09, 2003 : Otto Bruggeman + * Implemented inline differences (deactivated until KDE3.2 has been branched) + * added support for the -x and -X options to diff (deactivated until KDE3.2 has been branched) + * Various other code cleanups/reindenting + +Nov 09, 2003 : Otto Bruggeman + * Code cleanups + +Nov 02, 2003 : Otto Bruggeman + * Fixed some more scrolling problems + lastItem->scrollId(), add lastItem->maxHeight() and substract the minScrollId() + That is the maxScrollId i need in the QScrollBar, took me long enough... + +Oct 05, 2003 : Otto Bruggeman + * Fixed the scrolling problems, a stupid regression i introduced, i cant simplify mathematic expressions apparently + * Added an implementation for double clicking a difference in the view, but it is not properly connected yet + void contentsMouseDoubleClickEvent ( QMouseEvent* ); + * Fixed embedding in Konqueror by implementing openURL() + * Removed m_maxScrollId, it is not necessary and only costs time, QScrollView::contentsHeight() does the same + * Fixed some more warnings about unused variables + * Fixed the initial drawing of the vertical and horizontal scrollbar + +Oct 04, 2003 : Otto Bruggeman + * Added a call to m_modelList->openDirAndDiff to openDirAndDiff + * Fixed some error strings by swapping the %# thingies + * Added some useless debug output + * Fixed KompareModelList::openDirAndDiff to use the right models variable (m_models instead of models) + +Oct 03, 2003 : Otto Bruggeman + * Fixed ApplyAll and UnApplyAll, stupid copy and paste error + * Fixed some warnings about signed and unsigned + * Fixed some warnings about unused variables + * Fixed some redrawing issues in the connection widget + +Sep 27, 2003 : Otto Bruggeman + * Fixed the redrawing problems in the connect widget with a QTimer::singleShot() + * Undid a stupid commit that changed the keyboard shortcuts for next and previous difference + * Fixed another bug in the navigation part that made it emit a signal twice + * Fixed a bug in the listview drawing, still one left that i cant seem to solve :( + +Sep 27, 2003 : Otto Bruggeman + * Moved the apply and navigation actions into the komparemodellist + * Fixed Ingo's problem with the next and prev difference KActions + +Sep 26, 2003 : Otto Bruggeman + * Added a struct Info in the Kompare namespace. This one contains all the info about what kompare is doing + * Fixed splitting the path string in diffmodel + * Fixed showing the path in komparenavtreepart in the directory listviews + +Sep 24, 2003 : Otto Bruggeman + * Fixes opening diffs, comparing files after moving all that code around + +Sep 23, 2003 : Otto Bruggeman + * Moved a lot of url downloading to the kompare part and moved the opening and reading of the downloads to komparemodellist + +Sep 22, 2003 : Otto Bruggeman + * Added openStdin() to KompareShell + * Fixed stupid implicit conversion from QString to QStringList in kompare_part.cpp + * Added openDiff( QStringList ) to the interface and to the part + +Sep 14, 2003 : Otto Bruggeman + * Fixed exit status of the kompare process + +Sep 13, 2003 : Otto Bruggeman + * Removed some files that apparently came back after the merge + +Sep 07, 2003 : Otto Bruggeman + * Some changes to the interface. Made the copy ctor and assignment operator + and added a private d-pointer + * Removed the use of all deprecated methods and replaced them with undeprecated ones :) + +Sep 02, 2003 : Scott Wheeler + * Made the interface pure virtual + +Sep 01, 2003 : Scott Wheeler + * Fixed constness of the KompareModelList constructor + * Fixed another 2 warnings about comapring signed with unsigned ints + * Fixed the initialization of the difault var + +Aug 27, 2003 : Otto Bruggeman + * After shitloads of trouble here finally some fixes for the stupid desktop + file stuff + * Fixes for when there are not enough args for a certain commandline option. + +Aug 22, 2003 : Otto Bruggeman + * Fixed converting tabs to spaces in the view, i totally screwed up + * View settings now get applied to the view after pressing ok. + (Maybe i should make them apply on APlly instead of OK) + +Aug 13, 2003 : Otto Bruggeman + * Komkommertijd :) InitialPreference=10 for kompare.desktop as + requested + +Aug 10, 2003 : Otto Bruggeman + * Backported Helge Deller's changes from head to make_it_cool + (kompare_shell.cpp 1.33 -> 1.34). This is about roaming user fixes. + Thanks Helge ! + +Jul 19, 2003 : Otto Bruggeman + * Backported Ingo Klocker's changes from head to make_it_cool + (kompare_shell.cpp 1.34 -> 1.35). This is about being able to + configure the shortcuts from kompare_part as well. Thanks Ingo ! + +Jun 29, 2003 : Otto Bruggeman + * Fixed bug 58144 by adding a check for comparing dirs, in that case + destinationURL is a directory and not a file name so we need to + recreate the filename. This involved changing some code to use a + different enum value, so i hope i did it the right way, session + management may be broken now when the session was stored with 3.1.2 + and restarted with 3.1.3. But that is unfortunately unfixable with a + kconf_update script. + +Jun 29, 2003 : Otto Bruggeman + * Removed a lot of commented code since it is no longer used and will + never be used again. + * Added 2 methods to the interface: openDiff3(KURL) and + openDiff3(QStringList) + * Fixed context diff parsing as indicated in bugreport 57774 + (the example works now, hope there are no regressions) + * Removed all references to MiscSettings and MiscPrefs. + These classes will disappear RSN. + * Fixed the history saving of the urls in the kompare dialog + * Parser is no longer a static class but one that needs to be + instanciated + * Added ViewSettings to KompareProcess, maybe it is better to merge the + diff and view settings into one class. + +May 3, 2003 : Otto Bruggeman + * Implemented support for -I in the regular diff options (the one in + the kompare options dialog) + * Fixed the braindamage i created in main.cpp so that kompare no + longer stalls because of a missing mainwindow + * Made the kcomparedialog more generic and renamed it to + kompareurldialog so i can reuse it for blending too + * Removed some braindamage in the kompare/Makefile.am + * Some compile fixes because of changes to the CXXFLAGS + (QRegExp::match cant be used anymore, and some other old style stuff) + * Added an action to the menu for blending + * moved Open file (or in this case Open Diff) to the top of the file + menu + * Fixed the accel conflict in the file menu between open diff and + compare files + +Apr 30, 2003 : Otto Bruggeman + * Implemented blending of a diff file with the original file + * Renamed General* View* (more appropriate) + * Renamed m_models into m_modelList since it is more appropriate in komparepart + * Small fixes to the view, but they break more than they fix :( + * Added commandline options for comparing, opening a diff file and + blending + +Apr 20, 2003 : Otto Bruggeman + * Fixed bug 54264 with a statusbar that gets too wide when long + filenames are used + * Fixed the missing endline problem in the parser (bug 56552) + * Fixed all copyright years (probably too many but hey i'll change + those files some time this year so it will be valid :P) + * Added support for using a different diff program (Bug 55573) + * Added support for using a different tabsize in the viewer (Bug 38776) + * The interface is now final i guess so this fixes bug 42849, not + every method is implemented but i'll get to them eventually. + +Apr 19, 2003 : Otto Bruggeman + * Fixed bug 56322 where openURL did not clear the models when called + again with a new diff + +Aug 9, 2002 : Otto Bruggeman + * Fixed the whatsthis text for the compare button in the compare dialog + * Fixed the history of the comboboxes in the compare dialog + * Put the komparemodellist and all needed classes in a Diff2 namespace + * Implemented a better parser design (see parser.cpp/h) + * Removed the need to directly link to the komparepart for the shellapp + * Removed the need to link directly to the komparepart for the navigationpart + * Added support for perforce diffs in the new Parser classes + * Added a push design for the modified status instead of a pull design + * Added an interface to the Komparepart so people can use that to + reuse the komparepart + +Jul 15, 2002 : Otto Bruggeman + * Fixed normal diff a bit more, filenames dont work yet + * Removed some code duplication + * Fixed diff output parsing with Common subdirectories in it + * Fixed Copyright years in the about box (thanks Carsten Niehaus) + * Removed the K3ShellProcess and replaced it with a K3Process + +Feb 18, 2002 : Otto Bruggeman + * Fixed scrolling with a wheel mouse in the kompare(list)view and + connectwidget and added a config option for the number of lines + that is scrolled per wheelscroll. + * Fixed the history somehow in the compare dialog. + * Implemented the separate directory/file widget. + * Implemented reading from stdin by using - as file on the commandline. + * Partly implemented a better way for ed and rcs parsing, i'll + improve this before KDE 3.0 is released + +Jan 10, 2002 : Otto Bruggeman + Comparing directories works now :) You can select them from the begin + dialog, and select a directory and then press ok. It will enter the + directory but dont select a file so it keeps the directory. + Known bug here is that directories need a trailing slash :( + +Oct 07, 2001 : Otto Bruggeman + Fix crash when part is not found, basically dont use kapp->quit() + but use exit(int). Would be interested to know why it crashes though, + the bt gave nothing meaningful here. I should have compiled kompare with + debug code. + +Sep 17/18, 2001 : Otto Bruggeman + Fixed some stuff dont know what anymore (writing this on oct 7) + Probably some more fixes for the klibloader. + +Sep 17, 2001 : Otto Bruggeman + Moved to kdesdk and renamed to kompare with preservation of history. + Changed almost every occurence of kdiff to kompare (not in this file). + +Sep 08, 2001 : Otto Bruggeman + Removed the qt3back dir, changed everything over to qt3, + qlist->qptrlist, qlistconstiterator->qptrlistconstiterator + +Jul 29, 2001 : John Firebaugh + Add some tests. + Add the qregexp3 backport. + Use qregexp3 for diff parsing -- soooo much cleaner. + All the diff options work. + +Jul 28, 2001 : John Firebaugh + Directories can be selected in the compare dialog + New base clase KDiff, holds some common stuff + Use an enum for format in preferences + Implement a save options dialog, displayed at "Save .diff" + The diff can be run in any directory, the paths to source + and destination will be automatically determined from this. + Save all. + +Jul 25, 2001 : John Firebaugh + Prompt to save changes on close + Show [modified] caption + Clean up internal save mechanism + +Jul 14, 2001 : John Firebaugh + New menu item "Swap source and destination". + Make empty selection work. + +Jul 13, 2001 : John Firebaugh + Text view now works in compare mode. + Fix clicking difference to select it. + Don't scroll to difference when clicking to select it. + Give the diff view a nice frame. + +Jul 12, 2001 : Otto Bruggeman + Stats work now, maybe they need more info but i dont know what yet. + Will think some more about it. + +Jul 12, 2001 : John Firebaugh + When comparing files, you can apply or unapply changes and save + the result. + New menu item "Show Text View" (loads the diff in embedded text viewer). + Better status notification. + Set the window caption when comparing. + +Jun 27, 2001 : John Firebaugh + Ported main view to QListView + Remove obsolete files + Clicking a difference in the main view selects it + Better scrolling + +Jun 24, 2001 : John Firebaugh + Coverted to dock window and added navigation tree in a dock. + Multiple file diffs are now supported. Each file will show up + as an item in the tree, with differences as children. + +Jun 22, 2001 : Otto Bruggeman + Tried implementing rcs and ed but they dont work atm, same for show + diffstats, will fix that asap. + +May 22, 2001 : John Firebaugh + Reworking of most of the view code. Looks pretty. + +May 18, 2001 : Otto Bruggeman + Context seems to work, implemented saving... might have some problems + left (saving that is) + +May 15, 2001 : John Firebaugh + Make the settings work for all windows. Probably some more changes :) + +May 14, 2001 : Otto Bruggeman + context diff does not work atm, there is some problem with the separa- + tion of old and new. Maybe the old and new needs to be reintegrated. + I fixed some functions and now diffmodel does no longer need static + functions. All loading is done from the kdiffpart and that is where + save should go as well. Removed determineDiffFormat because it is not + needed anymore. + +May 13, 2001 : Otto Bruggeman + contextdiff is better implemented it finds all stuff in the diff atm + but it does not work. + +May 04, 2001 : Otto Bruggeman + cleaned up the code by moving the part to a subdir + halfassed implementation of contextdiff, will update later today + +Apr 10, 2001 : John Firebaugh + use new model/view architecture (not completely implemented yet) + NOTE: it will (should) compile, but you won't see any differences... a + work in progress + +Apr 05, 2001 : Otto Bruggeman + Implemented the ability to move from chuck to chunk in the htmlview + Cleaned up the preferences, squashed a few bugs + +Apr 04, 2001 : Otto Bruggeman + Normal format works as well + +Apr 04, 2001 : Otto Bruggeman + Finally implemented the preferences menu... i still lack some nice + icons for it but that will be solved in the near future... + +Mar 25, 2001 : Otto Bruggeman + Moved the application icons to the pics dir + +Mar 20, 2001 : Otto Bruggeman + Fixed a stupid bug that caused the last line in the rightview not + to be colored. + Implemented slots for using the KHistoryCombo in the views to select + files with. + Still a nasty bug with regard to the initial directory in the + KFileDialog, needs to be fixed asap but i dont know the cause. + Still an error in the historylist and completionlist items. They are + not shown correctly. + +Mar 19, 2001 : Otto Bruggeman + Added most of the preferences dialog + Some speed improvements + Some fixes to use the last used directory in KFileDialog diff --git a/kompare/DESIGN b/kompare/DESIGN new file mode 100644 index 00000000..fde8a5c9 --- /dev/null +++ b/kompare/DESIGN @@ -0,0 +1,35 @@ +Kompare General design: + +Kompare is split up into 4 parts: +- A shell around the parts +- A library with the modellist and the parser +- The navigation tree which uses the library +- The view part that also uses the library + +The diffmodel is comparable to a document and the view part is comparable +to the view and the komparemodellist is comparable to a documentmanager. +The navtree can be viewed as a document view manager. +The model is fully separated from the view and all communication goes +through signals and slots. The view gets a model that contains all differences +for the compared files A and B. The view gets this model from the modellist, +the central entity in the part. + +There is an interface to the komparepart that can be used in other programs, +simply link to the libkompareinterface.la and call its methods after you have +instanciated a komparepart. There is also a "hidden" signal and slot interface +that can be connected to from the shell app to get some information, and an +interface for communication between the navigation part and the komparepart. + +There is no need to use the interface for the communication between the +navigation and kompare parts in the shell app. + +Kompare has some debug areas: + +8100 kompare +8101 kompare (libs) +8102 kompare (shell) +8103 kompare (part) +8104 kompare (list view) +8105 kompare (nav view) +8106 kompare (connect widget) + diff --git a/kompare/Mainpage.dox b/kompare/Mainpage.dox new file mode 100644 index 00000000..da4f925c --- /dev/null +++ b/kompare/Mainpage.dox @@ -0,0 +1,19 @@ +/** @mainpage Kompare +* +* Kompare is a graphical difference viewer that allows you to visualize changes +* to a file. Whether you're a developer comparing source code, or you just want +* to see the difference between that research paper draft and the final document, +* Kompare is the tool you need. +* +* Kompare can +* - Compare two text files +* - Recursively compare directories +* - View patches generated by diff +* - Merge a patch into an existing directory +* +* Kompare can either be used as a standalone application, or embedded in another +* application - e.g. as a patch viewer in konqueror or in KDevelop. +* +* Kompare is part of the KDE SDK package, and it is released at the same time as +* the rest of KDE SDK and KDE. +*/ diff --git a/kompare/Messages.sh b/kompare/Messages.sh new file mode 100644 index 00000000..808ec391 --- /dev/null +++ b/kompare/Messages.sh @@ -0,0 +1,3 @@ +#! /bin/sh +$EXTRACTRC *.rc */*.rc */*.ui >> rc.cpp || exit 11 +$XGETTEXT `find . -name "*.cpp" -o -name "*.h"` -o $podir/kompare.pot diff --git a/kompare/README b/kompare/README new file mode 100644 index 00000000..af036f57 --- /dev/null +++ b/kompare/README @@ -0,0 +1,16 @@ +Kompare 2.0 + +Kompare is a program to view the differences between files. Features include: + + * comparison of files or directories via a graphical interface + * bezier-based connection widget lets you see both source and destination + as they really appear + * graphical viewing of patch files in normal, context, unified and + diff formats + * interactive application of differences + * full network transparency + * ability to view plain-text diff output in embedded viewer + * easy navigation of multiple-file diffs with dockable navigation tree + * graphical interface to commonly used diff command line options + * switch source and destination with one command + * diff statistics diff --git a/kompare/SRCBUILD b/kompare/SRCBUILD deleted file mode 100644 index f58472fc..00000000 --- a/kompare/SRCBUILD +++ /dev/null @@ -1,21 +0,0 @@ -# Maintainer: Ivailo Monev - -version=4.14.3 -description='Diff/Patch Frontend' -depends=('kde-runtime' 'libkomparediff2') -makedepends=('cmake' 'automoc4') -sources=("http://download.kde.org/stable/$version/src/kompare-$version.tar.xz") - -src_compile() { - mkdir "$SOURCE_DIR/build" && cd "$SOURCE_DIR/build" - cmake ../kompare-$version \ - -DCMAKE_BUILD_TYPE=Release \ - -DKDE4_BUILD_TESTS=OFF \ - -DCMAKE_INSTALL_PREFIX= - make -} - -src_install() { - cd "$SOURCE_DIR/build" - make DESTDIR=$INSTALL_DIR install -} diff --git a/kompare/TODO b/kompare/TODO new file mode 100644 index 00000000..e87a574f --- /dev/null +++ b/kompare/TODO @@ -0,0 +1,34 @@ +!!! Must do before a merge back into HEAD !!! +* Write a kconfupdate script to convert the kconfigfile + * the diff options now have their own group + * the view options now have their own group + * the Recent Files group has been renamed to Recent Compare Files + +Very important: +* Make sure unified and context actually work again ! +* Implement the rest of the interface (compare3 etc.) +* Make sure the rest works too ! Still some problems parsing some files +* Other various things that need to be fixed asap (before 3.2 is released) + +In descending order of importance: +* Add a kompare manual/document +* add support for handediting conflicts +* session management (should almost work) +* implement normal, ed and rcs format (normal got busted) +* merge text view GUI +* clicking in the connect widget should select a difference (tricky!) +* add support for incremental patch saving (i'll explain if anyone wants to know) +* Add support for diff3 (that is comparing 3 files at once) + +Special requests: +Puetzk: +* Editing the TO pane +Falk: +* Allow only part of a change to be applied to the other file +Harlekin: +* Add search functionality, either in source, dest or both at the same time +HarryF: +* emit clicked() signal when on a diff with line number +aseigo: +* merge from dest to source instead of only from source to dest +(Workaround: swap source with destination and then apply the differences) diff --git a/kompare/doc/CMakeLists.txt b/kompare/doc/CMakeLists.txt new file mode 100644 index 00000000..06d89873 --- /dev/null +++ b/kompare/doc/CMakeLists.txt @@ -0,0 +1,4 @@ +########### install files ############### +# +# +kde4_create_handbook(index.docbook INSTALL_DESTINATION ${HTML_INSTALL_DIR}/en SUBDIR kompare) diff --git a/kompare/doc/dock.png b/kompare/doc/dock.png new file mode 100644 index 00000000..39931b8a Binary files /dev/null and b/kompare/doc/dock.png differ diff --git a/kompare/doc/index.docbook b/kompare/doc/index.docbook new file mode 100644 index 00000000..578d12a4 --- /dev/null +++ b/kompare/doc/index.docbook @@ -0,0 +1,909 @@ + + + + + + +]> + + + + + The &kompare; Handbook + + + +SeanWhellersean@inwords.co.za + + + + +2007 +Sean Wheller + + + &FDLNotice; + +2010-07-22 +&version; + + + + + +&kompare; is a &GUI; front-end program that enables differences between source files to be viewed and merged. +&kompare; can be used to compare differences on files or the contents of folders. &kompare; supports a variety +of diff formats and provide many options to customize the information level displayed. +This document describes &kompare; version &version;. + + + + +KDE +Kompare +Diff +Merge +Patch +Hunk + + + + + +Introduction + +When two or more people are working on a file and passing it back and forth between one another, it becomes difficult to +see what changes have been made to a new version or copy of the file. Opening the new copy and the original, side-by-side in the +application used to create it is one solution but, laborious, time consuming, and prone to error. This is where a program to show +differences, diffs for short, is useful. + +As would be expected, an appropriate name for such a program would be "diff". As it happens, the program diff is installed on +most &Linux;-based systems and is used for exactly this purpose. Developers often use diff, as a command line tool, to show differences +between versions of a source code file. However, the use of diff is not limited to showing differences in code source files, +it can be used on many text-based file types. + +Using diff from the command line can be confusing, learning the diff command syntax and deciphering the output can bewilder most people. +This is where &kompare; comes into play. Providing a graphical front-end to the diff program, the interface displays source and destination files +side-by-side with all differences automatically highlighted. From this starting point, changes in one file can be sequentially applied to the other file +on a selective and controlled basis. Not all changes need to be applied and if you do apply a change it can always be 'unapplied'. +When all required changes have been applied they can be saved and will display as normal in the original application used to create the file. + +In addition to displaying differences between a source and destination file, &kompare; can be used to create and view a special file called a 'diff'. +This file captures the differences between the two sources into a single file that can be used to view and apply changes to any other copy of the file. +For example, if two people are editing a document. The first person wants to make changes and send just the changes made to the second person. +Normally, the first person would send a complete copy of the modified document to the second person, who would then have to compare the modified document +side-by-side with unmodified version. The process for this is much like what we have described in the previous paragraphs. With &kompare; the first person +would first make a local copy of the file to be modified, then make changes and compare the original and modified copy. Now using &kompare; a diff file +can be created that captures only the changes made. This can be sent to the second person in place of a whole file containing the changes. + +Using &kompare; the second person can view the diff file, compare it to the local copy of the document and apply the changes made by the first person. +So the process can go on for many versions of the document, each person making changes, creating diffs, distributing them and applying them. +This process is commonly called "patching", a term taken from the program named "patch" which is another command line +tool specifically designed for the purpose of applying diff files. + +It sometimes happens that people edit a file at the same time. In this situation it is likely that people will make changes in the document at +exactly the same line. This creates a problem because, without applied caution, people could be overwriting each others work as they apply the diff files they receive. +Fortunately the developers of the diff and patch programs took this into consideration and so these tools will not allow such changes to be applied without manual intervention. +When this state is reached, it is known as a "conflict". &kompare; will display conflicts so that you can manually resolve them, deciding +which changes should be applied to which file. + +&kompare; is also a great program for comparison of file changes on a folder level. When used to compare folders &kompare; recursively examines subfolders +and their file contents for differences. In this use case, each file where differences are found are automatically opened and +listed by &kompare; where easy navigation between the various files is possible. + + + + +Using &kompare; + + +Getting Started + +This section provides instructions for starting &kompare; and provides a quick tour to the &kompare; main interface. + + +Starting &kompare; + +A shortcut for starting &kompare; can be found in the K menu in the Development group +DevelopmentKompare. + +When &kompare; starts the first thing it does is display a dialog from +which to select the files you wish to compare. Special settings for the properties of the diff and the appearance thereof can also be selected. +In the file form select a source and destination source to compare. This can be any two files, folders or a &URL; and a file. +Once the source and destination are selected click the Compare button. + +Once &kompare; has discovered the differences it will display the main interface. +When comparing two files or a URL and a file the process takes just a few seconds. However, when comparing folders +with many subfolders and files, this process can take awhile. + +For explanation of the options available from diff and appearance forms see . + + + +The Main Interface + +This section provides a quick tour of the main interface which is comprised of the following areas: + +Menus +Toolbar +Source and Destination Folders +Source and Destination Files +Source and Destination Line Changes +Source and Destination Text View +Statusbar + + + +Menus +&kompare; provides a menu driven interface. Explanation to the menu items and their options is provided in . + + + +Toolbar +The &kompare; toolbar provides shortcuts to the most frequently used diff and merge operations. +The toolbar orientation, text positioning, icon size properties and which shortcut icons are displayed can be customized from the +toolbar context menu accessed when right-clicking the toolbar with the mouse. The toolbar context menu also enables the toolbar to be hidden. +If the toolbar is hidden and you wish to unhide it, select SettingsShow Toolbar. + + + +Source and Destination Folders +The source folder and destination folder panes display the folders in which compared files reside. +When many subfolders are included in the comparison, then selecting a folder will display the first document in +that folder where a difference was found between the source and destination. + + + +Source and Destination Files +The source and destination file pane displays files where a difference was found for the currently selected source or destination folder. +When a folder has multiple documents containing differences, all documents with a difference are listed. The selected document is displayed. + + + +Source and Destination Line Changes +The source and destination line changes pane summarizes the differences found between the current source and destination documents. +Selecting a record within the pane highlights and selects the difference. This is a useful way to navigate and inspect long documents with many differences. + + + +Source and Destination View +The source and destination view is the main workspace of &kompare;. +The contents and highlighted differences of the currently selected source and destination file are displayed here with line numbers. + + + +Text View +The Text View is not displayed by default. It can be opened by selecting +SettingsShow Text View. + + + + +Statusbar +The status bar provides a summary of the current source and destination file or folder under comparison. +The status bar also reports the number of changes found in the current document and counts the differences that have been applied. +Furthermore, the status bar shows the overall number of documents containing differences and the current document number selected from this set. +For example, a comparison run over two folders may return 1890 files with differences. The currently selected document is number 18 of 1890. + + + + + +Viewing Differences + + +Managing Screen Real-Estate +&kompare; displays the source and destination file under using equal percentage of the main interface view work area. +This view area provides some features that help optimize use of screen real-estate while viewing differences, including: + + +Dual Scrollbars +The most obvious feature is that scrollbars are provided both on the right and bottom edges of the view area. +Using the scrollbars it is possible to move rapidly through the comparison. + + +Share Grip Handle +The vertical space between the source and destination view not only makes it possible to clearly see the start and end of lines in each of the panes, +but is also a grip handle that allows adjustment of percentage occupied between the source and destinate views that comprise the view pane. +To change pane size for one of the views, hover the mouse pointer over the grip handle then hold down the mouse button and drag left or right. +Naturally, increasing the area of one pane will decrease the area available to the opposite pane within the view panel area. +A second horizontal handle is available between the navigation panel and the source and destination view. + + +Docking +The navigation panel can be undocked from the main interface by clicking the + icon located top right of the panel. +This opens it in a window of its own, allowing you to move it across the screen. You can even hide the navigation panel by clicking the icon. +To display a hidden navigation bar again, click with the &RMB; into the menubar and select Navigation from the context menu. + + + +Statusbar Toggle + +The status bar of the view panel can be toggled on and off by selecting SettingsShow Statusbar. + + + + + + +Switching Source and Destination Views + +Sometimes it can be useful to consider what the file to which differences where to be applied as the source. +For example, when comparing two modified versions of a file and discovering that the one file has many more modifications that the other. +The file with more changed would be better as the source, since then fewer differences would need to be applied. +In this case select FileSwap Source with Destination. +This will switch the files displayed in all &kompare; panels. + + + +Displaying Difference Statistics +For a quick overview of the differences, select FileShow Statistics. +This will display the Diff Statistics dialog. The following information is provided: + + +Old file: +The file name of what is usually the source file or file that is unmodified. + + +New file: +The file name of what is usually the destination file or file that is modified and to which differences will be applied. + + +Format: +The diff format used to display the difference (see ). + + +Number of hunks: + +The number of hunks found in the difference. +A hunk is a chunk of lines that have been marked as different between +source and destination and may include context lines depending on the diff format Lines of Context value (see ). + + +Number of differences +The actual number of differences found, not hunks. A hunk can contain one or more differences +when the line change range and the context lines of any two or more changes overlap. + + + + + +Navigating the Difference View +&kompare; enables rapid navigation of differences on a file level and of multiple difference files when comparing folder trees. + + +Selecting a Difference +A difference can be selected using by: + +clicking a line in the Source and Destination Line Changes pane (top right of the main window). +clicking the highlighted difference in the View pane. +traversing the listed differences in a comparison (see ). + +When a difference is selected it is considered to be in focus and is displayed in a brighter color that non-selected differences. + + + +Traversing Differences +When a comparison finds many differences one of the best ways to approach reviewing them is to traverse the differences in a logical order, usually from top to bottom. +By default &kompare; selects the first difference found in a comparison. By selecting +DifferenceNext Difference +(&Ctrl;Down) the difference following the current selection is brought into focus. +To select the difference before the current difference +select DifferencePrevious Difference +(&Ctrl;Up). +In this way it is possible to traverse differences in an orderly manner, applying and unapply differences upon review. + + + +Switching Between Files +When a comparison is performed on folder level, many files may be found with differences. +A complete list of the files compared with difference found is provided in the Source and Destination Folders, +and Source and Destination Files panes. However, &kompare; displays differences between source and destination one comparison at time. +To switch between documents in this scenario the following options are available: + +Select the Source and Destination Folders pane to display file differences found in the +Source and Destination Files pane, then select a file. +Select DifferencePrevious File +(&Ctrl;PgUp) or +DifferenceNext File +(&Ctrl;PgDown) to +display the previous or next difference file found in the Source and Destination Files pane. + + + + + + + +Merging Differences + +&kompare; makes the task of applying and unapplying differences as simple as point and click. +Multiple apply and unapply operations can be performed on a difference as all operations are performed in memory and not written to the files on disk until the save operation is performed. + + +Applying a Difference +To apply a difference, click the highlighted difference region, then select +DifferenceApply Difference (Space). + + + +Unapplying a Difference +To unapply a difference, click the highlighted difference region previously applied, then select +DifferenceUnapply Difference (&Backspace;). + + + +Applying All Differences +After reviewing differences between files and finding all acceptable it is possible apply them all with a single operation by selecting +DifferenceApply All (&Ctrl;A). + + + +Unapplying All Differences +To revert all differences that have been applied previously select +DifferenceUnapply All (&Ctrl;U). + + + +Saving Changes +Once differences have been applied they can be saved by selecting +FileSave or +FileSave All. +Applied differences are saved to both the source and destination file. + + + + +Working with Diff Files +Diff files contain only the changes made between files, or a set of files within a folder system, and may or may not contain a number of context lines before and after line changes. +The sum of a line change and its context lines is known a hunk. A diff file therefore may contain multiple hunks from one or more files. +When the context lines of two or more hunks overlap, they are considered a single hunk. Diff files can be used to: + +Apply the changes contained in the hunks to an original file. +Apply the changes contained in the hunks to a file or set of original files within a folder system. +Modified before being applied to an original file or set of original files within a folder system. + + + +Creating a Diff +To create a diff file a comparison must be displayed in &kompare;. Assuming this is the case, then select FileSave Diff... +This will display the Diff Options dialog (see for more information on diff formats and options). +After configuring these options, click the Save button and save the diff to a file with the extension .diff. + + + +Displaying a Diff +It is possible to display the contents of a diff file within &kompare; by opening the diff file from FileOpen Diff.... +When viewing a diff file the hunks between the source and destination file are shown, remember that only the hunks are shown, no unmodified lines will be shown. +In some cases a diff file is created with 0 lines of context. In this case only the changed lines will be displayed. +When a diff file contains hunks from multiple files &kompare; displays the hunks from each file one at a time and you can +switch between files as though they were real files even though this information is only provided by the diff file contents. + + + +Applying Differences in a Diff File +When viewing differences in a diff file it is possible to apply difference as you would when comparing source and destination files (see ). + + + +Blending a &URL; with a Diff +In cases where a diff file is provided it is possible to compare the hunks in the diff against a file or folder. +To do this select FileBlend URL with Diff.... +Then input the File/Folder and Diff Output paths. +When viewing differences between a source file and a diff file it is possible to apply difference as you would when comparing source and destination files (see ). + + + + + + +Configuring Preferences + +&kompare; enables users to set appearance preferences for difference formatting in the main interface and set behavioural properties of the diff program. +The Preferences dialog can be accessed by selecting +SettingsConfigure &kompare;.... + +To configure preferences for appearance select the View menu item (see ). + +To configure preferences for diff program properties select the Diff menu item (see ). + + +View Settings +The View page in the Preferences dialog displays the Appearance +and Fonts tabbed forms. + + +Appearance +The Appearance form provides controls to manage the Colors used +to denote difference in the main interface, behaviour of the Mouse Wheel when jogging up and down +and how Tabs to Spaces conversion is managed. + +&kompare; Appearance Settings + + + + + + &kompare; Appearance Settings + + + + +Color Group +To adjust color preferences used when displaying differences, click the color button to display the Select Color dialog for the following states: + +Removed color +Lines that have been removed, do not exist, between source and destination. + + +Changed color +Lines that have been changed, modified, between source and destination. + + +Added color +Lines that have been added between source and destination. + + +Applied color +Any of the above states where the difference has been applied between source and destination. + + + +Mouse Wheel + +Number of lines +The number of lines to jog the differences when turning the mouse wheel forward or backward. + + + +Tabs to Spaces + +Number of spaces to convert a tab character to +Convert each tab character to n space characters. + + + + + +Fonts + +&kompare; Fonts Settings + + + + + + &kompare; Fonts Settings + + + +Select the font family and size to display when displaying differences. + + + + +Diff Settings +The Diff page in the Preferences dialog displays the Diff, +Format, Options and Exclude tabbed forms. These forms can be used to configure the +behavioural properties of the Diff program. + + +Diff + +&kompare; Diff Settings + + + + + + &kompare; Diff Settings + + + +The command used to run the diff program (default diff). + + + +Format + +&kompare; Format Settings + + + + + + &kompare; Format Settings + + + +Adjust options for the Output Format and number of Lines of Context. + +Output Format + +Context + +The context output format adds several lines of context around the lines that differ. + + + + +Normal + +The normal output format displays differing lines without any surrounding lines of context. + + + + +Unified + +The unified output format is a variation on the context format. It is considered better than context because the +output is more compact than that of context as it omits redundant context lines. + + + + + +Lines of Context + +Number of context lines + +When performing a diff with context or unified output format use this parameter to control the number of context lines included. + + + + + + +Options + +&kompare; Options Settings + + + + + + &kompare; Options Settings + + + +The Options tab form allows configuration of the options supported by the diff program. + +General + +Treat new files as empty +With this option enabled diff will treat a file that only exists in one of +the directories as empty in the other directory. This means that the file is +compared with an empty file and because of this will appear as one big +insertion or deletion. + + +Look for smaller changes +Forces diff to display changes in case, punctuation, space, &etc; when checked. + + +Optimize for large files +Switches diff to process files with high-speed when checked. + + +Ignore changes in case +Lower and Uppercase character changes are omitted when this option is checked. + + + + +Ignore regexp +Ignore lines matching a regular expression. + + + +Whitespace + +Expand tabs to spaces in output +When checked diff outputs will converts tab characters to the number of spaces defined in the +Preferences dialog View menu Tabs to Spaces option. + + + +Ignore added or removed empty lines +lines of zero length that differ between source and destination are ignored when this option is checked. + + +Ignore changes in the amount of whitespace +White space before, after and between lines may change depending on different editors. +When this option is checked such changes are ignored. + + +Ignore all whitespace +when checked white space differences are completely ignored. + + +Ignore changes due to tab expansion +when checked white space resulting from tab characters is ignored. + + + + + +Exclude +The Exclude form enables use of the filter options provided by the diff program. + +&kompare; Exclude Settings + + + + + + &kompare; Exclude Settings + + + + +File Pattern to Exclude + +File Pattern to Exclude +Exclude files based on wild card filtering + + + +File with Filenames to Exclude + +File with Filenames to Exclude +Define the filter based on the content of an externally managed file. + + + + + + + + +Command Reference + + +The File Menu + + + + +&Ctrl;O + +FileOpen Diff... +Displays the Open dialog. + + + + + +&Ctrl;C + +FileCompare Files... +Displays the Compare Files or Folders dialog. + + + + + +&Ctrl;B + +FileBlend URL with Diff... +Displays the Blend File/Folder with diff Output dialog. + + + + + +&Ctrl;S + +FileSave +Writes applied differences to current source and or destination file. + + + +FileSave All +Writes applied differences to all source and or destination files. + + + +FileSave Diff... +Displays the Diff Options dialog to define diff format and options. + + + +FileSwap Source with Destination +Changes source and destination. + + + + +FileShow Statistics +Displays the Display Statistics dialog. + + + + + +&Ctrl;Q + +FileQuit +Exits &kompare;. + + + + + +The Difference Menu + + + + +&Ctrl;U + +DifferenceUnapply All +Unapply all differences previously applied between source and destination. + + + + +&Backspace; +DifferenceUnapply Difference +Revert a selected difference previously applied. + + + + +Space +DifferenceApply Difference +Apply a selected difference. + + + + + +&Ctrl;A + +DifferenceApply All +Apply all differences between source and destination. + + + + + +&Ctrl;PgUp + +DifferencePrevious File +Make the previous difference, in the list of differences, the current file in the view pane. + + + + + +&Ctrl;PgDown + +DifferenceNext File +Make the next difference, in the list of differences, the current file in the view pane. + + + + + +&Ctrl;Up + +DifferencePrevious Difference +Select the difference above the currently selected difference. + + + + + +&Ctrl;Down + +DifferenceNext Difference +Select the difference below the currently selected difference. + + + + + +The Settings Menu + + +SettingsShow Toolbar +Toggle the toolbar display ON/OFF. + + +SettingsShow Statusbar +Toggle the status bar display ON/OFF. + + +SettingsShow Text View +Display the Text View pane. + + +SettingsConfigure Shortcuts... +Display the Configure Shortcuts dialog. + + +SettingsConfigure Toolbars... +Display the Configure Toolbar. + + +SettingsConfigure &kompare;... +Display the &kompare; Preference dialog. + + + + + +The Help Menu + +&help.menu.documentation; + + + + + +Credits and License + + +&kompare; + + +Program copyright 2001-2004, &John.Firebaugh; &John.Firebaugh.mail; +and Otto Bruggeman otto.bruggeman@home.nl + + + +Documentation Copyright © 2007 Sean Wheller sean@inwords.co.za + + + + +&underFDL; +&underGPL; + + + + +Installation + + +How to obtain &kompare; + +&install.intro.documentation; + + + + + +Compilation and Installation + +&install.compile.documentation; + + + + + + +&documentation.index; + + + diff --git a/kompare/doc/settings-diff1.png b/kompare/doc/settings-diff1.png new file mode 100644 index 00000000..3f799bae Binary files /dev/null and b/kompare/doc/settings-diff1.png differ diff --git a/kompare/doc/settings-diff2.png b/kompare/doc/settings-diff2.png new file mode 100644 index 00000000..24fd40ee Binary files /dev/null and b/kompare/doc/settings-diff2.png differ diff --git a/kompare/doc/settings-diff3.png b/kompare/doc/settings-diff3.png new file mode 100644 index 00000000..65d0e9f3 Binary files /dev/null and b/kompare/doc/settings-diff3.png differ diff --git a/kompare/doc/settings-diff4.png b/kompare/doc/settings-diff4.png new file mode 100644 index 00000000..0c728e6d Binary files /dev/null and b/kompare/doc/settings-diff4.png differ diff --git a/kompare/doc/settings-view1.png b/kompare/doc/settings-view1.png new file mode 100644 index 00000000..5f592edd Binary files /dev/null and b/kompare/doc/settings-view1.png differ diff --git a/kompare/doc/settings-view2.png b/kompare/doc/settings-view2.png new file mode 100644 index 00000000..5ee00ecc Binary files /dev/null and b/kompare/doc/settings-view2.png differ diff --git a/kompare/doc/undock.png b/kompare/doc/undock.png new file mode 100644 index 00000000..f1a797ba Binary files /dev/null and b/kompare/doc/undock.png differ diff --git a/kompare/interfaces/CMakeLists.txt b/kompare/interfaces/CMakeLists.txt new file mode 100644 index 00000000..4bb0c6c5 --- /dev/null +++ b/kompare/interfaces/CMakeLists.txt @@ -0,0 +1,18 @@ + + + + +########### next target ############### + +set(kompareinterface_LIB_SRCS kompareinterface.cpp ) + + +kde4_add_library(kompareinterface SHARED ${kompareinterface_LIB_SRCS}) + +target_link_libraries(kompareinterface ${KDE4_KDECORE_LIBS} ) + +set_target_properties(kompareinterface PROPERTIES VERSION ${GENERIC_LIB_VERSION} SOVERSION ${GENERIC_LIB_SOVERSION} ) +install(TARGETS kompareinterface ${INSTALL_TARGETS_DEFAULT_ARGS} ) +install(FILES kompareinterface.h DESTINATION ${INCLUDE_INSTALL_DIR}/kompare COMPONENT Devel ) + + diff --git a/kompare/interfaces/kompareinterface.cpp b/kompare/interfaces/kompareinterface.cpp new file mode 100644 index 00000000..1acdd0fd --- /dev/null +++ b/kompare/interfaces/kompareinterface.cpp @@ -0,0 +1,76 @@ +/* + Copyright 2002-2004 Otto Bruggeman + + This program is free software; you can redistribute it and/or + modify it under the 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 "kompareinterface.h" + +class KompareInterfacePrivate +{ +public: + KompareInterfacePrivate(); + ~KompareInterfacePrivate(); + KompareInterfacePrivate( const KompareInterfacePrivate& ); + KompareInterfacePrivate& operator=( const KompareInterfacePrivate& ); + +protected: + // Add all variables for the KompareInterface class here and access them through the kip pointer +}; + +KompareInterfacePrivate::KompareInterfacePrivate() +{ +} + +KompareInterfacePrivate::~KompareInterfacePrivate() +{ +} + +KompareInterfacePrivate::KompareInterfacePrivate( const KompareInterfacePrivate& /*kip*/ ) +{ +} + +KompareInterfacePrivate& KompareInterfacePrivate::operator=(const KompareInterfacePrivate& /*kip*/ ) +{ + return *this; +} + +KompareInterface::KompareInterface() +{ + kip = new KompareInterfacePrivate(); +} + +KompareInterface::~KompareInterface() +{ + delete kip; +} + +KompareInterface::KompareInterface( const KompareInterface& ki ) +{ + kip = new KompareInterfacePrivate( *(ki.kip) ); +} + +KompareInterface& KompareInterface::operator=( const KompareInterface& ki ) +{ + kip = ki.kip; + return *this; +} + +void KompareInterface::setEncoding( const QString& encoding ) +{ + m_encoding = encoding; +} + diff --git a/kompare/interfaces/kompareinterface.h b/kompare/interfaces/kompareinterface.h new file mode 100644 index 00000000..a28d209b --- /dev/null +++ b/kompare/interfaces/kompareinterface.h @@ -0,0 +1,133 @@ +/* + Copyright 2002-2003 Otto Bruggeman + + This program is free software; you can redistribute it and/or + modify it under the 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 _KOMPARE_INTERFACE_H +#define _KOMPARE_INTERFACE_H + +#include +#include +#include + +class KConfig; +class KUrl; + +class KompareInterfacePrivate; + +class KDE_EXPORT KompareInterface +{ +public: + KompareInterface(); + virtual ~KompareInterface(); + +protected: + KompareInterface( const KompareInterface& ); + KompareInterface& operator=(const KompareInterface& ); + +public: + /** + * Open and parse the diff file at url. + */ + virtual bool openDiff( const KUrl& diffUrl ) = 0; + + /** + * Open and parse the supplied diff output + */ + virtual bool openDiff( const QString& diffOutput ) = 0; + + /** + * Open and parse the diff3 file at url. + */ + virtual bool openDiff3( const KUrl& diff3Url ) = 0; + + /** + * Open and parse the supplied diff3 output + */ + virtual bool openDiff3( const QString& diff3Output ) = 0; + + /** + * Compare, with diff, source with destination, can also be used if you do not + * know what source and destination are. The part will try to figure out what + * they are (directory, file, diff output file) and call the + * appropriate method(s) + */ + virtual void compare( const KUrl& sourceFile, const KUrl& destinationFile ) = 0; + + /** + * Compare a Source file to a custom Destination string + */ + virtual void compareFileString( const KUrl & sourceFile, const QString & destination) = 0; + + /** + * Compare a custom Source string to a Destination file + */ + virtual void compareStringFile( const QString & source, const KUrl & destinationFile) = 0; + + /** + * Compare, with diff, source with destination files + */ + virtual void compareFiles( const KUrl& sourceFile, const KUrl& destinationFile ) = 0; + + /** + * Compare, with diff, source with destination directories + */ + virtual void compareDirs ( const KUrl& sourceDir, const KUrl& destinationDir ) = 0; + + /** + * Compare, with diff3, originalFile with changedFile1 and changedFile2 + */ + virtual void compare3Files( const KUrl& originalFile, const KUrl& changedFile1, const KUrl& changedFile2 ) = 0; + + /** + * This will show the file and the file with the diff applied + */ + virtual void openFileAndDiff( const KUrl& file, const KUrl& diffFile ) = 0; + + /** + * This will show the directory and the directory with the diff applied + */ + virtual void openDirAndDiff ( const KUrl& dir, const KUrl& diffFile ) = 0; + + /** + * This will set the encoding to use for all files that are read or for the diffoutput + */ + virtual void setEncoding( const QString& encoding ); + +public: + /** + * Warning this should be in class Part in KDE 4.0, not here ! + * Around that time the methods will disappear here + */ + virtual int readProperties( KConfig* config ) = 0; + virtual int saveProperties( KConfig* config ) = 0; + + /** + * Warning this should be in class ReadWritePart in KDE 4.0, not here ! + * Around that time the method will disappear here + */ + virtual bool queryClose() = 0; + +protected: + // Add all variables to the KompareInterfacePrivate class and access them through the kip pointer + KompareInterfacePrivate* kip; + QString m_encoding; +}; + +Q_DECLARE_INTERFACE(KompareInterface, "com.kde.Kompare.KompareInterface/4.0") + +#endif /* _KOMPARE_INTERFACE_H */ diff --git a/kompare/interfaces/kompareinterfaceexport.h b/kompare/interfaces/kompareinterfaceexport.h new file mode 100644 index 00000000..eed5c85e --- /dev/null +++ b/kompare/interfaces/kompareinterfaceexport.h @@ -0,0 +1,37 @@ +/*************************************************************************** + * Copyright 2007 Andreas Pakulat * + * Copyright 2006 Matt Rogers * + * Copyright 2004 Jarosław Staniek * + * * + * 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 KOMPAREINTERFACEEXPORT_H +#define KOMPAREINTERFACEEXPORT_H + +/* needed for KDE_EXPORT macros */ +#include + +#ifndef DIFF2_EXPORT +# ifdef MAKE_DIFF2_LIB +# define DIFF2_EXPORT KDE_EXPORT +# else +# define DIFF2_EXPORT KDE_IMPORT +# endif +#endif + +#endif // KOMPAREINTERFACEEXPORT_H + diff --git a/kompare/kompare.desktop b/kompare/kompare.desktop new file mode 100755 index 00000000..9274b9c9 --- /dev/null +++ b/kompare/kompare.desktop @@ -0,0 +1,138 @@ +# KDE Config File +[Desktop Entry] +Type=Application +Name=Kompare +Name[af]=K-vergelyk +Name[ast]=Kompare +Name[bg]=Kompare +Name[br]=Kompare +Name[bs]=Kompare +Name[ca]=Kompare +Name[ca@valencia]=Kompare +Name[cs]=Kompare +Name[cy]=Kompare +Name[da]=Kompare +Name[de]=Kompare +Name[el]=Kompare +Name[en_GB]=Kompare +Name[eo]=Komparilo +Name[es]=Kompare +Name[et]=Kompare +Name[eu]=Kompare +Name[fi]=Kompare +Name[fr]=Kompare +Name[ga]=Kompare +Name[gl]=Kompare +Name[he]=Kompare +Name[hr]=Kompare +Name[hu]=Kompare +Name[is]=Kompare +Name[it]=Kompare +Name[ja]=Kompare +Name[kk]=Kompare +Name[km]=Kompare +Name[ko]=Kompare +Name[lt]=Kompare +Name[lv]=Kompare +Name[mr]=कंपेअर +Name[ms]=Kompare +Name[nb]=Kompare +Name[nds]=Kompare +Name[ne]=तुलना +Name[nl]=Kompare +Name[nn]=Kompare +Name[pa]=ਕੇ-ਤੁਲਨਾ +Name[pl]=Kompare +Name[pt]=Kompare +Name[pt_BR]=Kompare +Name[ro]=Kompare +Name[ru]=Kompare +Name[sk]=Kompare +Name[sl]=Kompare +Name[sr]=К‑поређење +Name[sr@ijekavian]=К‑поређење +Name[sr@ijekavianlatin]=K‑poređenje +Name[sr@latin]=K‑poređenje +Name[sv]=Kompare +Name[ta]=கோம்பெர் +Name[tg]=Kompare +Name[tr]=Kompare +Name[ug]=Kompare +Name[uk]=Kompare +Name[vi]=Kompare +Name[wa]=Kompare +Name[xh]=Kompare +Name[x-test]=xxKomparexx +Name[zh_CN]=Kompare +Name[zh_TW]=Kompare +GenericName=Diff/Patch Frontend +GenericName[af]=Diff/Lap Voorprogram +GenericName[ast]=Interface Diff/Patch +GenericName[bg]=Интерфейс за Diff/Patch +GenericName[bs]=Poređenje datoteka za KDE pomoću Diff/Patch +GenericName[ca]=Frontal del Diff/Patch +GenericName[ca@valencia]=Frontal del Diff/Patch +GenericName[cs]=Rozhraní pro Diff/Patch +GenericName[cy]=Blaen Gwahaniaethau/Clytiau +GenericName[da]=Diff/patch-grænseflade +GenericName[de]=Oberfläche für Diff und Patch +GenericName[el]=Πρόγραμμα Diff/Patch +GenericName[en_GB]=Diff/Patch Frontend +GenericName[eo]=Fasado por la programoj "diff" kaj "patch" +GenericName[es]=Interfaz Diff/Patch +GenericName[et]=Diff/patch kasutajaliides +GenericName[eu]=Desberdintasun/Adabaki interfazea +GenericName[fa]=پایانه Diff/کژنه +GenericName[fi]=Diff/Patch-käyttöliittymä +GenericName[fr]=Interface graphique pour « Diff » et « Patch » +GenericName[ga]=Comhéadan Diff/Patch +GenericName[gl]=Interface para Diff/Patch +GenericName[he]=ממשק ל-Diff/Patch +GenericName[hr]=Sučelje za Diff/Patch +GenericName[hu]=Grafikus diff/patch +GenericName[is]=Myndrænt viðmót á Diff/Patch +GenericName[it]=Interfaccia per diff e patch +GenericName[ja]=Diff/Patch フロントエンド +GenericName[kk]=Diff/Patch интерфейсі +GenericName[km]=កម្មវិធី​ផ្នែក​ខាងមុខរបស់​ Diff/Patch +GenericName[ko]=Diff/Patch 프론트엔드 +GenericName[lt]=Diff/Patch naudotojo sąsaja +GenericName[lv]=Diff/Patch priekšpuse +GenericName[mr]=डिफ/पेच फ्रंटएन्ड +GenericName[ms]=Bahagian Depan Beza/Tampal +GenericName[nb]=Diff-/Patch-grensesnitt +GenericName[nds]=Böversiet för "diff" un "patch" +GenericName[ne]=Diff/Patch फ्रन्टइन्ड +GenericName[nl]=Diff/Patch-hulpprogramma +GenericName[nn]=Diff-/Patch-grensesnitt +GenericName[pa]=Diff/ਪੈਂਚ ਫਰੰਟਐਂਡ +GenericName[pl]=Interfejs dla programów diff i patch +GenericName[pt]=Interface do Diff/Patch +GenericName[pt_BR]=Interface do Diff/Patch +GenericName[ro]=Interfață grafică pentru „diff” și „patch” +GenericName[ru]=Утилита сравнения файлов +GenericName[sk]=Rozhranie Diff/Patch +GenericName[sl]=Začelje za diff/patch +GenericName[sr]=Прочеље за diff и patch +GenericName[sr@ijekavian]=Прочеље за diff и patch +GenericName[sr@ijekavianlatin]=Pročelje za diff i patch +GenericName[sr@latin]=Pročelje za diff i patch +GenericName[sv]=Gränssnitt för Diff/Patch +GenericName[ta]=டிப்/ஒட்டு முன்பகுதி +GenericName[tg]=Утилитаи баробаркунии файлҳо +GenericName[tr]=Diff/Patch Önyüzü +GenericName[ug]=سېلىشتۇرۇش/ياماش(Diff/Patch) نىڭ ئالدى ئۇچى +GenericName[uk]=Інтерфейс до diff/patch +GenericName[vi]=Diff/Patch Frontend +GenericName[xh]=Diff/Patch Frontend +GenericName[x-test]=xxDiff/Patch Frontendxx +GenericName[zh_CN]=Diff/Patch 前端 +GenericName[zh_TW]=Diff/Patch 前端 +MimeType=text/x-patch; +Exec=kompare -caption %c -o %U +Icon=kompare +X-DocPath=kompare/index.html +Terminal=false +X-DBUS-StartupType=Multi +Categories=Qt;KDE;Development; +InitialPreference=10 diff --git a/kompare/kompare_shell.cpp b/kompare/kompare_shell.cpp new file mode 100644 index 00000000..9d220857 --- /dev/null +++ b/kompare/kompare_shell.cpp @@ -0,0 +1,468 @@ +/*************************************************************************** + kompare_shell.cpp + ----------------- + begin : Sun Mar 4 2001 + Copyright 2001-2004,2009 Otto Bruggeman + Copyright 2001-2003 John Firebaugh + Copyright 2007-2011 Kevin Kofler +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 "kompare_shell.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +// #include +#include + +#include "kompareinterface.h" +#include "kompareurldialog.h" + +#define ID_N_OF_N_DIFFERENCES 1 +#define ID_N_OF_N_FILES 2 +#define ID_GENERAL 3 + +KompareShell::KompareShell() + : KParts::MainWindow( ), + m_textViewPart( 0 ), + m_textViewWidget( 0 ) +{ + if ( !initialGeometrySet() ) + resize( 800, 480 ); + + // set the shell's ui resource file + setXMLFile("kompareui.rc"); + + // then, setup our actions + setupActions(); + setupStatusBar(); + + m_viewPart = KMimeTypeTrader::createInstanceFromQuery("text/x-patch", "Kompare/ViewPart", this); + + if ( m_viewPart ) + { + setCentralWidget( m_viewPart->widget() ); + // and integrate the part's GUI with the shell's + createGUI(m_viewPart); + } + else + { + // if we couldn't load our Part, we exit since the Shell by + // itself can't do anything useful + KMessageBox::error(this, i18n( "Could not load our KompareViewPart." ) ); + exit(2); + } + + m_navTreeDock = new QDockWidget( i18n( "Navigation" ), this ); + m_navTreeDock->setObjectName( "Navigation" ); + + // This part is implemented in KompareNavTreePart + m_navTreePart = KServiceTypeTrader::createInstanceFromQuery + ("KParts/ReadOnlyPart", "'Kompare/NavigationPart' in ServiceTypes", m_navTreeDock); + + if ( m_navTreePart ) + { + m_navTreeDock->setWidget( m_navTreePart->widget() ); + addDockWidget( Qt::TopDockWidgetArea, m_navTreeDock ); +// m_navTreeDock->manualDock( m_mainViewDock, KDockWidget::DockTop, 20 ); + } + else + { + // if we couldn't load our Part, we exit since the Shell by + // itself can't do anything useful + KMessageBox::error(this, i18n( "Could not load our KompareNavigationPart." ) ); + exit(4); + } + + // Hook up the inter part communication + connect( m_viewPart, SIGNAL( modelsChanged(const Diff2::DiffModelList*) ), + m_navTreePart, SLOT( slotModelsChanged( const Diff2::DiffModelList*) ) ); + + connect( m_viewPart, SIGNAL( kompareInfo(Kompare::Info*) ), + m_navTreePart, SLOT( slotKompareInfo(Kompare::Info*) ) ); + + connect( m_navTreePart, SIGNAL( selectionChanged(const Diff2::DiffModel*, const Diff2::Difference*) ), + m_viewPart, SIGNAL( selectionChanged(const Diff2::DiffModel*, const Diff2::Difference*) ) ); + connect( m_viewPart, SIGNAL( setSelection(const Diff2::DiffModel*, const Diff2::Difference*) ), + m_navTreePart, SLOT( slotSetSelection(const Diff2::DiffModel*, const Diff2::Difference*) ) ); + + connect( m_navTreePart, SIGNAL( selectionChanged(const Diff2::Difference*) ), + m_viewPart, SIGNAL( selectionChanged(const Diff2::Difference*) ) ); + connect( m_viewPart, SIGNAL( setSelection(const Diff2::Difference*) ), + m_navTreePart, SLOT( slotSetSelection(const Diff2::Difference*) ) ); + + // This is the interpart interface, it is signal and slot based so no "real" nterface here + // All you have to do is connect the parts from your application. + // These just point to the method with the same name in the KompareModelList or get called + // from the method with the same name in KompareModelList. + + // There is currently no applying possible from the navtreepart to the viewpart + connect( m_viewPart, SIGNAL(applyDifference(bool)), + m_navTreePart, SLOT(slotApplyDifference(bool)) ); + connect( m_viewPart, SIGNAL(applyAllDifferences(bool)), + m_navTreePart, SLOT(slotApplyAllDifferences(bool)) ); + connect( m_viewPart, SIGNAL(applyDifference(const Diff2::Difference*, bool)), + m_navTreePart, SLOT(slotApplyDifference(const Diff2::Difference*, bool)) ); + + // Hook up the KomparePart -> KompareShell communication + connect( m_viewPart, SIGNAL( setStatusBarModelInfo( int, int, int, int, int ) ), + this, SLOT( slotUpdateStatusBar( int, int, int, int, int ) ) ); + connect( m_viewPart, SIGNAL( setStatusBarText(const QString&) ), + this, SLOT( slotSetStatusBarText(const QString&) ) ); + + connect( m_viewPart, SIGNAL(diffString(const QString&)), + this, SLOT(slotSetDiffString(const QString&)) ); + + // Read basic main-view settings, and set to autosave + setAutoSaveSettings( "General Options" ); +} + +KompareShell::~KompareShell() +{ +} + +bool KompareShell::queryClose() +{ + bool rv = m_viewPart->queryClose(); + if ( rv ) + KGlobal::deref(); + return rv; +} + +void KompareShell::openDiff(const KUrl& url) +{ + kDebug(8102) << "Url = " << url.prettyUrl() << endl; + m_diffURL = url; + viewPart()->openDiff( url ); +} + +void KompareShell::openStdin() +{ + kDebug(8102) << "Using stdin to read the diff" << endl; + QFile file; + file.open( stdin, QIODevice::ReadOnly ); + QTextStream stream( &file ); + + QString diff = stream.readAll(); + + file.close(); + + viewPart()->openDiff( diff ); + +} + +void KompareShell::compare(const KUrl& source,const KUrl& destination ) +{ + m_sourceURL = source; + m_destinationURL = destination; + + viewPart()->compare( source, destination ); +} + +void KompareShell::blend( const KUrl& url1, const KUrl& diff ) +{ + m_sourceURL = url1; + m_destinationURL = diff; + + viewPart()->openDirAndDiff( url1, diff ); +} + +void KompareShell::setupActions() +{ + KAction *a; + a = actionCollection()->addAction(KStandardAction::Open, this, SLOT(slotFileOpen())); + a->setText( i18n( "&Open Diff..." ) ); + a = actionCollection()->addAction("file_compare_files", this, SLOT(slotFileCompareFiles())); + a->setIcon(KIcon("document-open")); + a->setText(i18n("&Compare Files...")); + a->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_C)); + a = actionCollection()->addAction("file_blend_url", this, SLOT(slotFileBlendURLAndDiff())); + a->setText(i18n("&Blend URL with Diff...")); + a->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_B)); + actionCollection()->addAction(KStandardAction::Quit, this, SLOT( slotFileClose() )); + + createStandardStatusBarAction(); + setStandardToolBarMenuEnabled(true); + m_showTextView = new KToggleAction(i18n("Show T&ext View"), this); +// needs a KGuiItem, also the doc says explicitly not to do this +// m_showTextView->setCheckedState(i18n("Hide T&ext View")); + actionCollection()->addAction("options_show_text_view", m_showTextView); + connect(m_showTextView, SIGNAL(triggered(bool)), SLOT(slotShowTextView())); + + KStandardAction::keyBindings(this, SLOT(optionsConfigureKeys()), actionCollection()); + KStandardAction::configureToolbars(this, SLOT(optionsConfigureToolbars()), actionCollection()); +} + +void KompareShell::setupStatusBar() +{ + // Made these entries permanent so they will appear on the right side + statusBar()->insertPermanentItem( i18n(" 0 of 0 differences "), ID_N_OF_N_DIFFERENCES, 0); + statusBar()->insertPermanentItem( i18n(" 0 of 0 files "), ID_N_OF_N_FILES, 0); + + m_generalLabel = new KSqueezedTextLabel( "", 0 ); + statusBar()->addWidget( m_generalLabel, 1 ); + m_generalLabel->setAlignment( Qt::AlignLeft ); +} + +void KompareShell::slotUpdateStatusBar( int modelIndex, int differenceIndex, int modelCount, int differenceCount, int appliedCount ) +{ + kDebug(8102) << "KompareShell::updateStatusBar()" << endl; + + QString fileStr; + QString diffStr; + + if ( modelIndex >= 0 ) + fileStr = i18np( " %2 of %1 file ", " %2 of %1 files ", modelCount, modelIndex + 1 ); + else + fileStr = i18np( " %1 file ", " %1 files ", modelCount ); + + if ( differenceIndex >= 0 ) + diffStr = i18np( " %2 of %1 difference, %3 applied ", " %2 of %1 differences, %3 applied ", differenceCount , + differenceIndex + 1, appliedCount ); + else + diffStr = i18np( " %1 difference ", " %1 differences ", differenceCount ); + + statusBar()->changeItem( fileStr, ID_N_OF_N_FILES ); + statusBar()->changeItem( diffStr, ID_N_OF_N_DIFFERENCES ); +} + +void KompareShell::slotSetStatusBarText( const QString& text ) +{ + m_generalLabel->setText( text ); +} + +void KompareShell::saveProperties(KConfigGroup &config) +{ + // The 'config' object points to the session managed + // config file. Anything you write here will be available + // later when this app is restored + if ( m_mode == Kompare::ComparingFiles ) + { + config.writeEntry( "Mode", "ComparingFiles" ); + config.writePathEntry( "SourceUrl", m_sourceURL.url() ); + config.writePathEntry( "DestinationUrl", m_destinationURL.url() ); + } + else if ( m_mode == Kompare::ShowingDiff ) + { + config.writeEntry( "Mode", "ShowingDiff" ); + config.writePathEntry( "DiffUrl", m_diffURL.url() ); + } + + viewPart()->saveProperties( config.config() ); +} + +void KompareShell::readProperties(const KConfigGroup &config) +{ + // The 'config' object points to the session managed + // config file. This function is automatically called whenever + // the app is being restored. Read in here whatever you wrote + // in 'saveProperties' + + QString mode = config.readEntry( "Mode", "ComparingFiles" ); + if ( mode == "ComparingFiles" ) + { + m_mode = Kompare::ComparingFiles; + m_sourceURL = config.readPathEntry( "SourceUrl", "" ); + m_destinationURL = config.readPathEntry( "DestinationFile", "" ); + + viewPart()->readProperties( const_cast(config.config()) ); + + viewPart()->compareFiles( m_sourceURL, m_destinationURL ); + } + else if ( mode == "ShowingDiff" ) + { + m_mode = Kompare::ShowingDiff; + m_diffURL = config.readPathEntry( "DiffUrl", "" ); + + viewPart()->readProperties( const_cast(config.config()) ); + + m_viewPart->openUrl( m_diffURL ); + } + else + { // just in case something weird has happened, don't restore the diff then + // Bruggie: or when some idiot like me changes the possible values for mode + // IOW, a nice candidate for a kconf_update thingy :) + viewPart()->readProperties( const_cast(config.config()) ); + } +} + +void KompareShell::slotFileOpen() +{ + // FIXME: use different filedialog which gets encoding + KUrl url = KFileDialog::getOpenUrl( KUrl(), "text/x-patch", this ); + if( !url.isEmpty() ) { + KompareShell* shell = new KompareShell(); + KGlobal::ref(); + shell->show(); + shell->openDiff( url ); + } +} + +void KompareShell::slotFileBlendURLAndDiff() +{ + KompareURLDialog dialog( this ); + + dialog.setCaption( i18n( "Blend File/Folder with diff Output" ) ); + dialog.setFirstGroupBoxTitle( i18n( "File/Folder" ) ); + dialog.setSecondGroupBoxTitle( i18n( "Diff Output" ) ); + + KGuiItem blendGuiItem( i18n( "Blend" ), QString(), i18n( "Blend this file or folder with the diff output" ), i18n( "If you have entered a file or folder name and a file that contains diff output in the fields in this dialog then this button will be enabled and pressing it will open kompare's main view where the output of the entered file or files from the folder are mixed with the diff output so you can then apply the difference(s) to a file or to the files. " ) ); + dialog.setButtonGuiItem( KDialog::Ok, blendGuiItem ); + + dialog.setGroup( "Recent Blend Files" ); + + dialog.setFirstURLRequesterMode( KFile::File|KFile::Directory|KFile::ExistingOnly ); + // diff output can not be a directory + dialog.setSecondURLRequesterMode( KFile::File|KFile::ExistingOnly ); + if ( dialog.exec() == QDialog::Accepted ) + { + m_sourceURL = dialog.getFirstURL(); + m_destinationURL = dialog.getSecondURL(); + // Leak??? + KompareShell* shell = new KompareShell(); + KGlobal::ref(); + shell->show(); + shell->viewPart()->setEncoding( dialog.encoding() ); + shell->blend( m_sourceURL, m_destinationURL ); + } +} + +void KompareShell::slotFileCompareFiles() +{ + KompareURLDialog dialog( this ); + + dialog.setCaption( i18n( "Compare Files or Folders" ) ); + dialog.setFirstGroupBoxTitle( i18n( "Source" ) ); + dialog.setSecondGroupBoxTitle( i18n( "Destination" ) ); + + KGuiItem compareGuiItem( i18n( "Compare" ), QString(), i18n( "Compare these files or folders" ), i18n( "If you have entered 2 filenames or 2 folders in the fields in this dialog then this button will be enabled and pressing it will start a comparison of the entered files or folders. " ) ); + dialog.setButtonGuiItem( KDialog::Ok, compareGuiItem ); + + dialog.setGroup( "Recent Compare Files" ); + + dialog.setFirstURLRequesterMode( KFile::File|KFile::Directory|KFile::ExistingOnly ); + dialog.setSecondURLRequesterMode( KFile::File|KFile::Directory|KFile::ExistingOnly ); + + if ( dialog.exec() == QDialog::Accepted ) + { + m_sourceURL = dialog.getFirstURL(); + m_destinationURL = dialog.getSecondURL(); + KompareShell* shell = new KompareShell(); + KGlobal::ref(); + shell->show(); + kDebug(8102) << "The encoding is: " << dialog.encoding() << endl; + shell->viewPart()->setEncoding( dialog.encoding() ); + shell->compare( m_sourceURL, m_destinationURL ); + } +} + +void KompareShell::slotFileClose() +{ + if ( m_viewPart->queryClose() ) + { + KGlobal::deref(); + } +} + +void KompareShell::slotShowTextView() +{ + if ( !m_textViewWidget ) + { + QString error; + + // FIXME: proper error checking + m_textViewWidget = new QDockWidget( i18n( "Text View" ), this ); + m_textViewWidget->setObjectName( "Text View" ); +// m_textViewWidget = createDockWidget( i18n("Text View"), SmallIcon( "text-x-generic") ); + m_textViewPart = KServiceTypeTrader::createInstanceFromQuery( + QString::fromLatin1("KTextEditor/Document"), + this, this, QString(), QVariantList(), &error ); + if ( m_textViewPart ) + { + m_textView = qobject_cast( m_textViewPart->createView( this ) ); + m_textViewWidget->setWidget( static_cast(m_textView) ); + m_textViewPart->setHighlightingMode( "Diff" ); + m_textViewPart->setText( m_diffString ); + } + m_textViewWidget->show(); + connect( m_textViewWidget, SIGNAL(visibilityChanged(bool)), SLOT(slotVisibilityChanged(bool)) ); + } + else if ( m_textViewWidget->isVisible() ) + m_textViewWidget->hide(); + else + m_textViewWidget->show(); + + addDockWidget( Qt::BottomDockWidgetArea, m_textViewWidget ); +// m_textViewWidget->manualDock( m_mainViewDock, KDockWidget:: DockCenter ); +} + +void KompareShell::slotVisibilityChanged( bool visible ) +{ + m_showTextView->setChecked( visible ); +} + +void KompareShell::slotSetDiffString( const QString& diffString ) +{ + if ( m_textViewPart ) + m_textViewPart->setText( diffString ); + m_diffString = diffString; +} + +void KompareShell::optionsConfigureKeys() +{ + KShortcutsDialog dlg( KShortcutsEditor::AllActions, KShortcutsEditor::LetterShortcutsAllowed, this ); + + dlg.addCollection( actionCollection() ); + if ( m_viewPart ) + dlg.addCollection( m_viewPart->actionCollection() ); + + dlg.configure(); +} + +void KompareShell::optionsConfigureToolbars() +{ + KConfigGroup group(KGlobal::config(), autoSaveGroup()); + saveMainWindowSettings(group); + // use the standard toolbar editor + KEditToolBar dlg(factory()); + connect(&dlg,SIGNAL(newToolbarConfig()),this,SLOT(newToolbarConfig())); + dlg.exec(); +} + +void KompareShell::newToolbarConfig() +{ + KConfigGroup group(KGlobal::config(), autoSaveGroup()); + applyMainWindowSettings(group); +} + +KompareInterface* KompareShell::viewPart() const +{ + return qobject_cast(m_viewPart); +} + +#include "kompare_shell.moc" diff --git a/kompare/kompare_shell.h b/kompare/kompare_shell.h new file mode 100644 index 00000000..de099ffb --- /dev/null +++ b/kompare/kompare_shell.h @@ -0,0 +1,150 @@ +/*************************************************************************** + kompare_shell.h + ---------------- + begin : Sun Mar 4 2001 + Copyright 2001-2003 Otto Bruggeman + Copyright 2001-2003 John Firebaugh + Copyright 2007-2011 Kevin Kofler +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 KOMPARESHELL_H +#define KOMPARESHELL_H + +#include +#include +#include + +class KompareInterface; +namespace KParts { + class ReadOnlyPart; + class ReadWritePart; +} +class KToggleAction; + +class KSqueezedTextLabel; +class KomparePart; +class KompareNavTreePart; + +namespace KTextEditor { + class Document; + class EditInterface; + class View; +} + +/** + * This is the application "Shell". It has a menubar, toolbar, and + * statusbar but relies on the "Part" to do all the real work. + * + * Adapted the shell a bit so it now handles separate view and navigation parts + * + * @short Application Shell + * @author John Firebaugh + * @author Otto Bruggeman + * @version 3.2.90 + */ +class KompareShell : public KParts::MainWindow +{ + Q_OBJECT +public: + /** + * Default Constructor + */ + KompareShell(); + + /** + * Default Destructor + */ + virtual ~KompareShell(); + + /** + * Use this method to load whatever file/URL you have + */ + void openDiff( const KUrl& url ); + + /** + * Use this method to load the diff from stdin + */ + void openStdin(); + + /** + * Use this method to compare 2 URLs (files or directories) + */ + void compare( const KUrl& source, const KUrl& destination ); + + /** + * Use this method to blend diff into url1 (file or directory) + */ + void blend( const KUrl& url1, const KUrl& diff ); + +public slots: + void slotUpdateStatusBar( int modelIndex, int differenceIndex, int modelCount, int differenceCount, int appliedCount ); + + KompareInterface* viewPart() const; + +protected: + virtual bool queryClose(); + + /** + * This method is called when it is time for the app to save its + * properties for session management purposes. + */ + void saveProperties(KConfigGroup &); + + /** + * This method is called when this app is restored. The KConfig + * object points to the session management config file that was saved + * with @ref saveProperties + */ + void readProperties(const KConfigGroup &); + +private slots: + void slotSetStatusBarText( const QString& text ); + void slotFileOpen(); + void slotFileCompareFiles(); + void slotFileBlendURLAndDiff(); + void slotShowTextView(); + void slotFileClose(); + void optionsConfigureKeys(); + void optionsConfigureToolbars(); + void slotSetDiffString( const QString& diffString ); + void newToolbarConfig(); + void slotVisibilityChanged( bool visible ); + +private: + void setupAccel(); + void setupActions(); + void setupStatusBar(); + +private: + KUrl m_sourceURL; + KUrl m_destinationURL; + KUrl m_diffURL; + + KParts::ReadWritePart* m_viewPart; + KParts::ReadOnlyPart* m_navTreePart; + KTextEditor::Document* m_textViewPart; + KTextEditor::View* m_textView; +// KTextEditor::EditInterface* m_textEditIface; + + QDockWidget* m_textViewWidget; + QDockWidget* m_navTreeDock; + + KToggleAction* m_showTextView; + + enum Kompare::Mode m_mode; + // This is the statusbarwidget for displaying the general stuff + KSqueezedTextLabel* m_generalLabel; + + QString m_diffString; +}; + +#endif // KOMPARE_H diff --git a/kompare/komparenavigationpart.desktop b/kompare/komparenavigationpart.desktop new file mode 100644 index 00000000..1e7f7669 --- /dev/null +++ b/kompare/komparenavigationpart.desktop @@ -0,0 +1,4 @@ +[Desktop Entry] +Type=ServiceType +X-KDE-ServiceType=Kompare/NavigationPart +X-KDE-Derived=KParts/ReadOnlyPart diff --git a/kompare/komparenavtreepart/CMakeLists.txt b/kompare/komparenavtreepart/CMakeLists.txt new file mode 100644 index 00000000..da52bc7d --- /dev/null +++ b/kompare/komparenavtreepart/CMakeLists.txt @@ -0,0 +1,22 @@ + +include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/../libdiff2 ${CMAKE_CURRENT_SOURCE_DIR}/../komparepart ) + + +########### next target ############### + +set(komparenavtreepart_PART_SRCS komparenavtreepart.cpp ) + + +kde4_add_plugin(komparenavtreepart ${komparenavtreepart_PART_SRCS}) + + + +target_link_libraries(komparenavtreepart ${KDE4_KPARTS_LIBS} ${LIBKOMPAREDIFF2_LIBRARIES} ) + +install(TARGETS komparenavtreepart DESTINATION ${PLUGIN_INSTALL_DIR} ) + + +########### install files ############### + +install( FILES komparenavtreepart.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) + diff --git a/kompare/komparenavtreepart/komparenavtreepart.cpp b/kompare/komparenavtreepart/komparenavtreepart.cpp new file mode 100644 index 00000000..d3bdc93d --- /dev/null +++ b/kompare/komparenavtreepart/komparenavtreepart.cpp @@ -0,0 +1,780 @@ +/*************************************************************************** + KompareNavTreePart.cpp + ---------------------- + begin : Sun Mar 4 2001 + Copyright 2001-2005,2009 Otto Bruggeman + Copyright 2001-2003 John Firebaugh + Copyright 2007-2011 Kevin Kofler +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 "komparenavtreepart.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "difference.h" +#include "diffmodel.h" +#include "diffmodellist.h" +#include "komparemodellist.h" + +#define COL_SOURCE 0 +#define COL_DESTINATION 1 +#define COL_DIFFERENCE 2 + +using namespace Diff2; + +KompareNavTreePart::KompareNavTreePart( QWidget* parentWidget, QObject* parent, const QVariantList& ) + : KParts::ReadOnlyPart( parent ), + m_splitter( 0 ), + m_modelList( 0 ), + m_srcDirTree( 0 ), + m_destDirTree( 0 ), + m_fileList( 0 ), + m_changesList( 0 ), + m_srcRootItem( 0 ), + m_destRootItem( 0 ), + m_selectedModel( 0 ), + m_selectedDifference( 0 ), + m_source( "" ), + m_destination( "" ), + m_info( 0 ) +{ + m_splitter = new QSplitter( Qt::Horizontal, parentWidget ); + + setWidget( m_splitter ); + + m_srcDirTree = new QTreeWidget( m_splitter ); + m_srcDirTree->setHeaderLabel( i18n("Source Folder") ); + m_srcDirTree->setRootIsDecorated( false ); + m_srcDirTree->setSortingEnabled( true ); + m_srcDirTree->sortByColumn( 0, Qt::AscendingOrder ); + + m_destDirTree = new QTreeWidget( m_splitter ); + m_destDirTree->setHeaderLabel( i18n("Destination Folder") ); + m_destDirTree->setRootIsDecorated( false ); + m_destDirTree->setSortingEnabled( true ); + m_destDirTree->sortByColumn( 0, Qt::AscendingOrder ); + + m_fileList = new QTreeWidget( m_splitter ); + m_fileList->setHeaderLabels( QStringList() << i18n("Source File") << i18n("Destination File") ); + m_fileList->setAllColumnsShowFocus( true ); + m_fileList->setRootIsDecorated( false ); + m_fileList->setSortingEnabled( true ); + m_fileList->sortByColumn( 0, Qt::AscendingOrder ); + + m_changesList = new QTreeWidget( m_splitter ); + m_changesList->setHeaderLabels( QStringList() << i18n("Source Line") << i18n("Destination Line") << i18n("Difference") ); + m_changesList->setAllColumnsShowFocus( true ); + m_changesList->setRootIsDecorated( false ); + m_changesList->setSortingEnabled( true ); + m_changesList->sortByColumn( 0, Qt::AscendingOrder ); + + connect( m_srcDirTree, SIGNAL(currentItemChanged( QTreeWidgetItem*, QTreeWidgetItem* )), + this, SLOT(slotSrcDirTreeSelectionChanged( QTreeWidgetItem* )) ); + connect( m_destDirTree, SIGNAL(currentItemChanged( QTreeWidgetItem*, QTreeWidgetItem* )), + this, SLOT(slotDestDirTreeSelectionChanged( QTreeWidgetItem* )) ); + connect( m_fileList, SIGNAL(currentItemChanged( QTreeWidgetItem*, QTreeWidgetItem* )), + this, SLOT(slotFileListSelectionChanged( QTreeWidgetItem* )) ); + connect( m_changesList, SIGNAL(currentItemChanged( QTreeWidgetItem*, QTreeWidgetItem* )), + this, SLOT(slotChangesListSelectionChanged( QTreeWidgetItem* )) ); +} + +KompareNavTreePart::~KompareNavTreePart() +{ + m_modelList = 0; + m_selectedModel = 0; + m_selectedDifference = 0; +} + +void KompareNavTreePart::slotKompareInfo( struct Kompare::Info* info ) +{ + m_info = info; +} + +void KompareNavTreePart::slotModelsChanged( const DiffModelList* modelList ) +{ + kDebug(8105) << "Models (" << modelList << ") have changed... scanning the models... " << endl; + + if ( modelList ) + { + m_modelList = modelList; + m_srcDirTree->clear(); + m_destDirTree->clear(); + m_fileList->clear(); + m_changesList->clear(); + buildTreeInMemory(); + } + else + { + m_modelList = modelList; + m_srcDirTree->clear(); + m_destDirTree->clear(); + m_fileList->clear(); + m_changesList->clear(); + } +} + +void KompareNavTreePart::buildTreeInMemory() +{ + kDebug(8105) << "BuildTreeInMemory called" << endl; + + if ( m_modelList->count() == 0 ) + { + kDebug(8105) << "No models... weird shit..." << endl; + return; // avoids a crash on clear() + } + + if ( m_info == 0 ) + { + kDebug(8105) << "No Info... weird shit..." << endl; + return; + } + + QString srcBase; + QString destBase; + + DiffModel* model; + model = m_modelList->first(); + m_selectedModel = 0L; + + switch ( m_info->mode ) + { + case Kompare::ShowingDiff: + // BUG: 107489 No common root because it is a multi directory relative path diff + // We need to detect this and create a different rootitem / or so or should we always add this? + // Trouble we run into is that the directories do not start with a / + // so we have an unknown top root dir + // Thinking some more about it i guess it is best to use "" as base and simply show some string + // like Unknown filesystem path as root text but only in the case of dirs starting without a / + srcBase = model->sourcePath(); + destBase = model->destinationPath(); + // FIXME: these tests will not work on windows, we need something else + if ( srcBase[0] != '/' ) + srcBase = ""; + if ( destBase[0] != '/' ) + destBase = ""; + break; + case Kompare::ComparingFiles: + srcBase = model->sourcePath(); + destBase = model->destinationPath(); + break; + case Kompare::ComparingDirs: + srcBase = m_info->localSource; + if ( !srcBase.endsWith( '/' ) ) + srcBase += '/'; + destBase = m_info->localDestination; + if ( !destBase.endsWith( '/' ) ) + destBase += '/'; + break; + case Kompare::BlendingFile: + case Kompare::BlendingDir: + default: + kDebug(8105) << "Oops needs to implement this..." << endl; + } + +// kDebug(8105) << "srcBase = " << srcBase << endl; +// kDebug(8105) << "destBase = " << destBase << endl; + + m_srcRootItem = new KDirLVI( m_srcDirTree, srcBase ); + m_destRootItem = new KDirLVI( m_destDirTree, destBase ); + + QString srcPath; + QString destPath; + + // Create the tree from the models + DiffModelListConstIterator modelIt = m_modelList->begin(); + DiffModelListConstIterator mEnd = m_modelList->end(); + + for ( ; modelIt != mEnd; ++modelIt ) + { + model = *modelIt; + srcPath = model->sourcePath(); + destPath = model->destinationPath(); + + kDebug(8105) << "srcPath = " << srcPath << endl; + kDebug(8105) << "destPath = " << destPath << endl; + m_srcRootItem->addModel( srcPath, model, &m_modelToSrcDirItemDict ); + m_destRootItem->addModel( destPath, model, &m_modelToDestDirItemDict ); + } +// m_srcDirTree->setSelected( m_srcDirTree->firstChild(), true ); +} + +void KompareNavTreePart::buildDirectoryTree() +{ +// FIXME: afaict this can be deleted +// kDebug(8105) << "BuildDirTree called" << endl; +} + +QString KompareNavTreePart::compareFromEndAndReturnSame( + const QString& string1, + const QString& string2 ) +{ + QString result; + + int srcLen = string1.length(); + int destLen = string2.length(); + + while ( srcLen != 0 && destLen != 0 ) + { + if ( string1[--srcLen] == string2[--destLen] ) + result.prepend( string1[srcLen] ); + else + break; + } + + if ( srcLen != 0 && destLen != 0 && result.startsWith( '/' ) ) + result = result.remove( 0, 1 ); // strip leading /, we need it later + + return result; +} + +void KompareNavTreePart::slotSetSelection( const DiffModel* model, const Difference* diff ) +{ + kDebug(8105) << "KompareNavTreePart::slotSetSelection model = " << model << ", diff = " << diff << endl; + if ( model == m_selectedModel ) + { + // model is the same, so no need to update that... + if ( diff != m_selectedDifference ) + { + m_selectedDifference = diff; + setSelectedDifference( diff ); + } + return; + } + + // model is different so we need to find the right dirs, file and changeitems to select + // if m_selectedModel == NULL then everything needs to be done as well + if ( !m_selectedModel || model->sourcePath() != m_selectedModel->sourcePath() ) + { // dirs are different, so we need to update the dirviews as well + m_selectedModel = model; + m_selectedDifference = diff; + + setSelectedDir( model ); + setSelectedFile( model ); + setSelectedDifference( diff ); + return; + } + + if ( !m_selectedModel || model->sourceFile() != m_selectedModel->sourceFile() ) + { + m_selectedModel = model; + setSelectedFile( model ); + + m_selectedDifference = diff; + setSelectedDifference( diff ); + } +} + +void KompareNavTreePart::setSelectedDir( const DiffModel* model ) +{ + KDirLVI* currentDir; + currentDir = m_modelToSrcDirItemDict[ model ]; + kDebug(8105) << "Manually setting selection in srcdirtree with currentDir = " << currentDir << endl; + m_srcDirTree->blockSignals( true ); + m_srcDirTree->setCurrentItem( currentDir ); + m_srcDirTree->scrollToItem( currentDir ); + m_srcDirTree->blockSignals( false ); + + currentDir = m_modelToDestDirItemDict[ model ]; + kDebug(8105) << "Manually setting selection in destdirtree with currentDir = " << currentDir << endl; + m_destDirTree->blockSignals( true ); + m_destDirTree->setCurrentItem( currentDir ); + m_destDirTree->scrollToItem( currentDir ); + m_destDirTree->blockSignals( false ); + + m_fileList->blockSignals( true ); + currentDir->fillFileList( m_fileList, &m_modelToFileItemDict ); + m_fileList->blockSignals( false ); +} + +void KompareNavTreePart::setSelectedFile( const DiffModel* model ) +{ + KFileLVI* currentFile; + currentFile = m_modelToFileItemDict[ model ]; + kDebug(8105) << "Manually setting selection in filelist" << endl; + m_fileList->blockSignals( true ); + m_fileList->setCurrentItem( currentFile ); + m_fileList->scrollToItem( currentFile ); + m_fileList->blockSignals( false ); + + m_changesList->blockSignals( true ); + currentFile->fillChangesList( m_changesList, &m_diffToChangeItemDict ); + m_changesList->blockSignals( false ); +} + +void KompareNavTreePart::setSelectedDifference( const Difference* diff ) +{ + KChangeLVI* currentDiff; + currentDiff = m_diffToChangeItemDict[ diff ]; + kDebug(8105) << "Manually setting selection in changeslist to " << currentDiff << endl; + m_changesList->blockSignals( true ); + m_changesList->setCurrentItem( currentDiff ); + m_changesList->scrollToItem( currentDiff ); + m_changesList->blockSignals( false ); +} + +void KompareNavTreePart::slotSetSelection( const Difference* diff ) +{ +// kDebug(8105) << "Scotty i need more power !!" << endl; + if ( m_selectedDifference != diff ) + { +// kDebug(8105) << "But sir, i am giving you all she's got" << endl; + m_selectedDifference = diff; + setSelectedDifference( diff ); + } +} + +void KompareNavTreePart::slotSrcDirTreeSelectionChanged( QTreeWidgetItem* item ) +{ + if (!item) + return; + + kDebug(8105) << "Sent by the sourceDirectoryTree with item = " << item << endl; + m_srcDirTree->scrollToItem( item ); + KDirLVI* dir = static_cast(item); + // order the dest tree view to set its selected item to the same as here + QString path; + // We start with an empty path and after the call path contains the full path + path = dir->fullPath( path ); + KDirLVI* selItem = m_destRootItem->setSelected( path ); + m_destDirTree->blockSignals( true ); + m_destDirTree->setCurrentItem( selItem ); + m_destDirTree->scrollToItem( selItem ); + m_destDirTree->blockSignals( false ); + // fill the changes list + dir->fillFileList( m_fileList, &m_modelToFileItemDict ); +} + +void KompareNavTreePart::slotDestDirTreeSelectionChanged( QTreeWidgetItem* item ) +{ + if (!item) + return; + + kDebug(8105) << "Sent by the destinationDirectoryTree with item = " << item << endl; + m_destDirTree->scrollToItem( item ); + KDirLVI* dir = static_cast(item); + // order the src tree view to set its selected item to the same as here + QString path; + // We start with an empty path and after the call path contains the full path + path = dir->fullPath( path ); + KDirLVI* selItem = m_srcRootItem->setSelected( path ); + m_srcDirTree->blockSignals( true ); + m_srcDirTree->setCurrentItem( selItem ); + m_srcDirTree->scrollToItem( selItem ); + m_srcDirTree->blockSignals( false ); + // fill the changes list + dir->fillFileList( m_fileList, &m_modelToFileItemDict ); +} + +void KompareNavTreePart::slotFileListSelectionChanged( QTreeWidgetItem* item ) +{ + if (!item) + return; + + kDebug(8105) << "Sent by the fileList with item = " << item << endl; + + KFileLVI* file = static_cast(item); + m_selectedModel = file->model(); + m_changesList->blockSignals( true ); + file->fillChangesList( m_changesList, &m_diffToChangeItemDict ); + m_changesList->blockSignals( false ); + + if ( m_changesList->currentItem() ) + { + // FIXME: This is ugly... + m_selectedDifference = (static_cast(m_changesList->currentItem()))->difference(); + } + + emit selectionChanged( m_selectedModel, m_selectedDifference ); +} + +void KompareNavTreePart::slotChangesListSelectionChanged( QTreeWidgetItem* item ) +{ + if (!item) + return; + + kDebug(8105) << "Sent by the changesList" << endl; + + KChangeLVI* change = static_cast(item); + m_selectedDifference = change->difference(); + + emit selectionChanged( m_selectedDifference ); +} + +void KompareNavTreePart::slotApplyDifference( bool /*apply*/ ) +{ + KChangeLVI* clvi = m_diffToChangeItemDict[m_selectedDifference]; + if ( clvi ) + clvi->setDifferenceText(); +} + +void KompareNavTreePart::slotApplyAllDifferences( bool /*apply*/ ) +{ + QHash::ConstIterator it = m_diffToChangeItemDict.constBegin(); + QHash::ConstIterator end = m_diffToChangeItemDict.constEnd(); + + kDebug(8105) << "m_diffToChangeItemDict.count() = " << m_diffToChangeItemDict.count() << endl; + + for ( ; it != end ; ++it ) + { + it.value()->setDifferenceText(); + } +} + +void KompareNavTreePart::slotApplyDifference( const Difference* diff, bool /*apply*/ ) +{ + // this applies to the currently selected difference + KChangeLVI* clvi = m_diffToChangeItemDict[diff]; + if ( clvi ) + clvi->setDifferenceText(); +} + +void KChangeLVI::setDifferenceText() +{ + QString text; + switch( m_difference->type() ) { + case Difference::Change: + // Shouldn't this simply be diff->sourceLineCount() ? + // because you change the _number of lines_ lines in source, not in dest + if( m_difference->applied() ) + text = i18np( "Applied: Changes made to %1 line undone", "Applied: Changes made to %1 lines undone", + m_difference->sourceLineCount() ); + else + text = i18np( "Changed %1 line", "Changed %1 lines", + m_difference->sourceLineCount() ); + break; + case Difference::Insert: + if( m_difference->applied() ) + text = i18np( "Applied: Insertion of %1 line undone", "Applied: Insertion of %1 lines undone", + m_difference->destinationLineCount() ); + else + text = i18np( "Inserted %1 line", "Inserted %1 lines", + m_difference->destinationLineCount() ); + break; + case Difference::Delete: + if( m_difference->applied() ) + text = i18np( "Applied: Deletion of %1 line undone", "Applied: Deletion of %1 lines undone", + m_difference->sourceLineCount() ); + else + text = i18np( "Deleted %1 line", "Deleted %1 lines", + m_difference->sourceLineCount() ); + break; + default: + kDebug(8105) << "Unknown or Unchanged enum value when checking for diff->type() in KChangeLVI's constructor" << endl; + text = ""; + } + + setText( 2, text ); +} + +KChangeLVI::KChangeLVI( QTreeWidget* parent, Difference* diff ) : QTreeWidgetItem( parent ) +{ + m_difference = diff; + + setText( 0, QString::number( diff->sourceLineNumber() ) ); + setText( 1, QString::number( diff->destinationLineNumber() ) ); + + setDifferenceText(); +} + +bool KChangeLVI::operator<( const QTreeWidgetItem& item ) const +{ + int column = treeWidget()->sortColumn(); + QString text1 = text(column); + QString text2 = item.text(column); + + // Compare the numbers. + if (column < 2 && text1.length() != text2.length()) + return text1.length() < text2.length(); + return text1 < text2; +} + +KChangeLVI::~KChangeLVI() +{ +} + +KFileLVI::KFileLVI( QTreeWidget* parent, DiffModel* model ) : QTreeWidgetItem( parent ) +{ + m_model = model; + + QString src = model->sourceFile(); + QString dst = model->destinationFile(); + + setText( 0, src ); + setText( 1, dst ); + setIcon( 0, SmallIcon( getIcon( src ) ) ); + setIcon( 1, SmallIcon( getIcon( dst ) ) ); +} + +bool KFileLVI::hasExtension(const QString& extensions, const QString& fileName) +{ + QStringList extList = extensions.split(' '); + foreach (const QString &ext, extList) { + if ( fileName.endsWith(ext, Qt::CaseInsensitive) ) { + return true; + } + } + return false; +} + +const QString KFileLVI::getIcon(const QString& fileName) +{ + // C++, C + if ( hasExtension( ".h .hpp", fileName ) ) { + return "text-x-c++hdr"; + } + if ( hasExtension( ".cpp", fileName ) ) { + return "text-x-c++src"; + } + if ( hasExtension( ".c", fileName ) ) { + return "text-x-csrc"; + } + // Python + if ( hasExtension( ".py .pyw", fileName ) ) { + return "text-x-python"; + } + // C# + if ( hasExtension( ".cs", fileName ) ) { + return "text-x-csharp"; + } + // Objective-C + if ( hasExtension( ".m", fileName ) ) { + return "text-x-objcsrc"; + } + // Java + if ( hasExtension( ".java", fileName ) ) { + return "text-x-java"; + } + // Script + if ( hasExtension( ".sh", fileName ) ) { + return "text-x-script"; + } + // Makefile + if ( hasExtension( ".cmake Makefile", fileName ) ) { + return "text-x-makefile"; + } + // Ada + if ( hasExtension( ".ada .ads .adb", fileName ) ) { + return "text-x-adasrc"; + } + // Pascal + if ( hasExtension( ".pas", fileName ) ) { + return "text-x-pascal"; + } + // Patch + if ( hasExtension( ".diff", fileName ) ) { + return "text-x-patch"; + } + // Tcl + if ( hasExtension( ".tcl", fileName ) ) { + return "text-x-tcl"; + } + // Text + if ( hasExtension( ".txt", fileName ) ) { + return "text-plain"; + } + // Xml + if ( hasExtension( ".xml", fileName ) ) { + return "text-xml"; + } + // unknown or no file extension + return "text-plain"; +} + +void KFileLVI::fillChangesList( QTreeWidget* changesList, QHash* diffToChangeItemDict ) +{ + changesList->clear(); + diffToChangeItemDict->clear(); + + DifferenceListConstIterator diffIt = m_model->differences()->constBegin(); + DifferenceListConstIterator dEnd = m_model->differences()->constEnd(); + + for ( ; diffIt != dEnd; ++diffIt ) + { + KChangeLVI* change = new KChangeLVI( changesList, *diffIt ); + diffToChangeItemDict->insert( *diffIt, change ); + } + + changesList->setCurrentItem( changesList->topLevelItem( 0 ) ); +} + +KFileLVI::~KFileLVI() +{ +} + +KDirLVI::KDirLVI( QTreeWidget* parent, QString& dir ) : QTreeWidgetItem( parent ) +{ +// kDebug(8105) << "KDirLVI (QTreeWidget) constructor called with dir = " << dir << endl; + m_rootItem = true; + m_dirName = dir; + setIcon( 0, SmallIcon( "folder" ) ); + setExpanded( true ); + if ( m_dirName.isEmpty() ) + setText( 0, i18n( "Unknown" ) ); + else + setText( 0, m_dirName ); +} + +KDirLVI::KDirLVI( KDirLVI* parent, QString& dir ) : QTreeWidgetItem( parent ) +{ +// kDebug(8105) << "KDirLVI (KDirLVI) constructor called with dir = " << dir << endl; + m_rootItem = false; + m_dirName = dir; + setIcon( 0, SmallIcon( "folder" ) ); + setExpanded( true ); + setText( 0, m_dirName ); +} + +// addModel always removes it own path from the beginning +void KDirLVI::addModel( QString& path, DiffModel* model, QHash* modelToDirItemDict ) +{ +// kDebug(8105) << "KDirLVI::addModel called with path = " << path << " from KDirLVI with m_dirName = " << m_dirName << endl; + + if ( !m_dirName.isEmpty() ) + { + if ( path.indexOf( m_dirName ) > -1 ) + path = path.remove( path.indexOf( m_dirName ), m_dirName.length() ); + } + +// kDebug(8105) << "Path after removal of own dir (\"" << m_dirName << "\") = " << path << endl; + + if ( path.isEmpty() ) { + m_modelList.append( model ); + modelToDirItemDict->insert( model, this ); + return; + } + + KDirLVI* child; + + QString dir = path.mid( 0, path.indexOf( "/", 0 ) + 1 ); + child = findChild( dir ); + if ( !child ) + { + // does not exist yet so make it +// kDebug(8105) << "KDirLVI::addModel creating new KDirLVI because not found" << endl; + child = new KDirLVI( this, dir ); + } + + child->addModel( path, model, modelToDirItemDict ); +} + +KDirLVI* KDirLVI::findChild( QString dir ) +{ +// kDebug(8105) << "KDirLVI::findChild called with dir = " << dir << endl; + KDirLVI* child; + if ( ( child = static_cast(this->child(0)) ) != 0L ) + { // has children, check if dir already exists, if so addModel + QTreeWidgetItemIterator it(child); + while (*it) { + child = static_cast(*it); + + if ( dir == child->dirName() ) + return child; + ++it; + } + } + + return 0L; +} + +void KDirLVI::fillFileList( QTreeWidget* fileList, QHash* modelToFileItemDict ) +{ + fileList->clear(); + + DiffModelListIterator modelIt = m_modelList.begin(); + DiffModelListIterator mEnd = m_modelList.end(); + for ( ;modelIt != mEnd; ++modelIt ) + { + KFileLVI* file = new KFileLVI( fileList, *modelIt ); + modelToFileItemDict->insert( *modelIt, file ); + } + + fileList->setCurrentItem( fileList->topLevelItem( 0 ) ); +} + +QString KDirLVI::fullPath( QString& path ) +{ +// if ( !path.isEmpty() ) +// kDebug(8105) << "KDirLVI::fullPath called with path = " << path << endl; +// else +// kDebug(8105) << "KDirLVI::fullPath called with empty path..." << endl; + + if ( m_rootItem ) // don't bother adding rootItem's dir... + return path; + + path = path.prepend( m_dirName ); + + KDirLVI* lviParent = dynamic_cast( parent() ); + if ( lviParent ) + { + path = lviParent->fullPath( path ); + } + + return path; +} + +KDirLVI* KDirLVI::setSelected( QString dir ) +{ +// kDebug(8105) << "KDirLVI::setSelected called with dir = " << dir << endl; + + // root item's dirName is never taken into account... remember that + if ( !m_rootItem ) + { + dir = dir.remove( 0, m_dirName.length() ); + } + + if ( dir.isEmpty() ) + { + return this; + } + KDirLVI* child = static_cast(this->child(0)); + if ( !child ) + return 0L; + + QTreeWidgetItemIterator it(child); + while (*it) { + child = static_cast(*it); + + if ( dir.startsWith( child->dirName() ) ) + return child->setSelected( dir ); + ++it; + } + + return 0L; +} + +KDirLVI::~KDirLVI() +{ + m_modelList.clear(); +} + +static KAboutData aboutData() +{ + KAboutData about("komparenavtreepart", 0, ki18n("KompareNavTreePart"), "1.2"); + about.addAuthor(ki18n("John Firebaugh"), ki18n("Author"), "jfirebaugh@kde.org"); + about.addAuthor(ki18n("Otto Bruggeman"), ki18n("Author"), "bruggie@gmail.com" ); + return about; +} + +K_PLUGIN_FACTORY(KompareNavTreePartFactory, + registerPlugin(); + ) +K_EXPORT_PLUGIN( KompareNavTreePartFactory(aboutData()) ) + +#include "komparenavtreepart.moc" diff --git a/kompare/komparenavtreepart/komparenavtreepart.desktop b/kompare/komparenavtreepart/komparenavtreepart.desktop new file mode 100644 index 00000000..b110c1a4 --- /dev/null +++ b/kompare/komparenavtreepart/komparenavtreepart.desktop @@ -0,0 +1,67 @@ +[Desktop Entry] +Name=KompareNavTreePart +Name[ast]=KompareNavTreePart +Name[bg]=KompareNavTreePart +Name[br]=KompareNavTreePart +Name[bs]=KompareNavTreePart +Name[ca]=KompareNavTreePart +Name[ca@valencia]=KompareNavTreePart +Name[cs]=KompareNavTreePart +Name[cy]=KompareNavTreePart +Name[da]=KompareNavTreePart +Name[de]=KompareNavTreePart +Name[el]=KompareNavTreePart +Name[en_GB]=KompareNavTreePart +Name[es]=KompareNavTreePart +Name[et]=KompareNavTreePart +Name[eu]=KompareNavTreePart +Name[fi]=KompareNavTreePart +Name[fr]=Composant de KompareNavTree +Name[ga]=KompareNavTreePart +Name[gl]=KompareNavTreePart +Name[he]=KompareNavTreePart +Name[hr]=KompareNavTreePart +Name[hu]=KompareNavTreePart +Name[is]=KompareNavTreePart +Name[it]=KompareNavTreePart +Name[ja]=KompareNavTreePart +Name[kk]=KompareNavTreePart +Name[km]=KompareNavTreePart +Name[ko]=KompareNavTreePart +Name[lt]=KompareNavTreePart +Name[lv]=KompareNavTreePart +Name[mr]=कंपेअर-नेव्ह-ट्री-पार्ट +Name[ms]=KompareNavTreePart +Name[nb]=KompareNavTreePart +Name[nds]=KompareNavTreePart +Name[ne]=KompareNavTreePart +Name[nl]=KompareNavTreePart +Name[nn]=KompareNavTreePart +Name[pa]=KompareNavTreePart +Name[pl]=Komponent drzewa nawigacyjnego Kompare +Name[pt]=KompareNavTreePart +Name[pt_BR]=KompareNavTreePart +Name[ro]=KompareNavTreePart +Name[ru]=KompareNavTreePart +Name[sk]=KompareNavTreePart +Name[sl]=KompareNavTreePart +Name[sq]=KompareNavTreePart +Name[sr]=KompareNavTreePart +Name[sr@ijekavian]=KompareNavTreePart +Name[sr@ijekavianlatin]=KompareNavTreePart +Name[sr@latin]=KompareNavTreePart +Name[sv]=Kompare-navigeringsträdsdel +Name[ta]=கோம்பெர் மரம் பாகம் +Name[tg]=KompareNavTreePart +Name[tr]=KompareNavTreePart +Name[ug]=KompareNavTreePart +Name[uk]=KompareNavTreePart +Name[xh]=KompareNavTreePart +Name[x-test]=xxKompareNavTreePartxx +Name[zh_CN]=KompareNavTreePart +Name[zh_TW]=KompareNavTreePart +MimeType=text/x-patch; +ServiceTypes=Kompare/NavigationPart +X-KDE-Library=komparenavtreepart +Type=Service +Icon=kompare diff --git a/kompare/komparenavtreepart/komparenavtreepart.h b/kompare/komparenavtreepart/komparenavtreepart.h new file mode 100644 index 00000000..eb08329b --- /dev/null +++ b/kompare/komparenavtreepart/komparenavtreepart.h @@ -0,0 +1,169 @@ +/*************************************************************************** + komparenavtreepart.h + -------------------- + begin : Mon Feb 26 2002 + Copyright 2001-2004 Otto Bruggeman + Copyright 2001-2003 John Firebaugh + Copyright 2007-2011 Kevin Kofler +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 KOMPARENAVTREEPART_H +#define KOMPARENAVTREEPART_H + +#include + +#include +#include +#include + +#include +#include +#include + +namespace Diff2 { +class DiffModel; +class Difference; +} + +class KDirLVI; +class KFileLVI; +class KChangeLVI; + +class KompareNavTreePart : public KParts::ReadOnlyPart +{ + Q_OBJECT + +public: + explicit KompareNavTreePart( QWidget* parentWidget, QObject* parent, const QVariantList& args ); + virtual ~KompareNavTreePart(); + +public: + virtual bool openFile() { return false; }; + +public slots: + void slotSetSelection( const Diff2::DiffModel* model, const Diff2::Difference* diff ); + void slotSetSelection( const Diff2::Difference* diff ); + void slotModelsChanged( const Diff2::DiffModelList* modelList ); + void slotKompareInfo( Kompare::Info* info ); + +signals: + void selectionChanged( const Diff2::DiffModel* model, const Diff2::Difference* diff ); + void selectionChanged( const Diff2::Difference* diff ); + +private slots: + void slotSrcDirTreeSelectionChanged ( QTreeWidgetItem* item ); + void slotDestDirTreeSelectionChanged( QTreeWidgetItem* item ); + void slotFileListSelectionChanged ( QTreeWidgetItem* item ); + void slotChangesListSelectionChanged( QTreeWidgetItem* item ); + + void slotApplyDifference( bool apply ); + void slotApplyAllDifferences( bool apply ); + void slotApplyDifference( const Diff2::Difference* diff, bool apply ); + + void buildTreeInMemory(); + +private: + void setSelectedDir( const Diff2::DiffModel* model ); + void setSelectedFile( const Diff2::DiffModel* model ); + void setSelectedDifference( const Diff2::Difference* diff ); + + void buildDirectoryTree(); + + QString compareFromEndAndReturnSame( const QString& string1, const QString& string2 ); + void addDirToTreeView( enum Kompare::Target, const QString& filename ); + + QTreeWidgetItem* findDirInDirTree( const QTreeWidgetItem* parent, const QString& dir ); + +private: + QSplitter* m_splitter; + const Diff2::DiffModelList* m_modelList; + + QHash m_diffToChangeItemDict; + QHash m_modelToFileItemDict; + QHash m_modelToSrcDirItemDict; + QHash m_modelToDestDirItemDict; + + QTreeWidget* m_srcDirTree; + QTreeWidget* m_destDirTree; + QTreeWidget* m_fileList; + QTreeWidget* m_changesList; + + KDirLVI* m_srcRootItem; + KDirLVI* m_destRootItem; + + const Diff2::DiffModel* m_selectedModel; + const Diff2::Difference* m_selectedDifference; + + QString m_source; + QString m_destination; + + struct Kompare::Info* m_info; +}; + +// These 3 classes are need to store the models into a tree so it is easier +// to extract the info we need for the navigation widgets + +class KChangeLVI : public QTreeWidgetItem +{ +public: + KChangeLVI( QTreeWidget* parent, Diff2::Difference* diff ); + ~KChangeLVI(); +public: + Diff2::Difference* difference() { return m_difference; }; + virtual bool operator<( const QTreeWidgetItem& item ) const; + + void setDifferenceText(); +private: + Diff2::Difference* m_difference; +}; + +class KFileLVI : public QTreeWidgetItem +{ +public: + KFileLVI( QTreeWidget* parent, Diff2::DiffModel* model ); + ~KFileLVI(); +public: + Diff2::DiffModel* model() { return m_model; }; + void fillChangesList( QTreeWidget* changesList, QHash* diffToChangeItemDict ); +private: + bool hasExtension(const QString& extensions, const QString& fileName); + const QString getIcon(const QString& fileName); +private: + Diff2::DiffModel* m_model; +}; + +class KDirLVI : public QTreeWidgetItem +{ +public: + KDirLVI( KDirLVI* parent, QString& dir ); + KDirLVI( QTreeWidget* parent, QString& dir ); + ~KDirLVI(); +public: + void addModel( QString& dir, Diff2::DiffModel* model, QHash* modelToDirItemDict ); + QString& dirName() { return m_dirName; }; + QString fullPath( QString& path ); + + KDirLVI* setSelected( QString dir ); + void setSelected( bool selected ) { QTreeWidgetItem::setSelected( selected ); } + + void fillFileList( QTreeWidget* fileList, QHash* modelToFileItemDict ); + bool isRootItem() { return m_rootItem; }; + +private: + KDirLVI* findChild( QString dir ); +private: + Diff2::DiffModelList m_modelList; + QString m_dirName; + bool m_rootItem; +}; + +#endif diff --git a/kompare/komparepart/CMakeLists.txt b/kompare/komparepart/CMakeLists.txt new file mode 100644 index 00000000..ee83458a --- /dev/null +++ b/kompare/komparepart/CMakeLists.txt @@ -0,0 +1,85 @@ + +include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/../libdialogpages ${CMAKE_CURRENT_SOURCE_DIR}/../interfaces ) + + +########### next target ############### + +set( komparepart_PART_SRCS + kompare_part.cpp + kompareconnectwidget.cpp + komparesplitter.cpp + komparelistview.cpp + kompareprefdlg.cpp + komparesaveoptionsbase.cpp + komparesaveoptionswidget.cpp + kompareview.cpp ) + + +kde4_add_ui_files(komparepart_PART_SRCS komparesaveoptionsbase.ui ) + +kde4_add_plugin(komparepart ${komparepart_PART_SRCS}) + + + +target_link_libraries(komparepart ${KDE4_KPARTS_LIBS} komparedialogpages ${LIBKOMPAREDIFF2_LIBRARIES} kompareinterface ) + +install(TARGETS komparepart DESTINATION ${PLUGIN_INSTALL_DIR} ) + + +########### install files ############### + +install( FILES komparepart.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) +install( FILES komparepartui.rc DESTINATION ${DATA_INSTALL_DIR}/kompare ) + + + + +#original Makefile.am contents follow: + +########################################################################## +## KPART SECTION +########################################################################## +# +#INCLUDES = \ +# -I$(top_srcdir)/kompare/libdialogpages \ +# -I$(top_srcdir)/kompare/interfaces \ +# $(all_includes) +# +#noinst_HEADERS = \ +# kompare_part.h \ +# komparesplitter.h \ +# kompareprefdlg.h \ +# komparelistview.h \ +# kompareconnectwidget.h \ +# komparesaveoptionsbase.h \ +# komparesaveoptionswidget.h \ +# kompare_qsplitter.h +# +## let automoc handle all of the meta source files (moc) +#METASOURCES = AUTO +# +#kde_module_LTLIBRARIES = libkomparepart.la +# +## the Part's source, library search path, and link libraries +#libkomparepart_la_SOURCES = \ +# kompare_part.cpp \ +# kompareconnectwidget.cpp \ +# komparesplitter.cpp \ +# komparelistview.cpp \ +# kompareprefdlg.cpp \ +# komparesaveoptionsbase.ui \ +# komparesaveoptionswidget.cpp +# +#libkomparepart_la_LDFLAGS = $(KDE_PLUGIN) $(all_libraries) +#libkomparepart_la_LIBADD = $(LIB_KPARTS) $(LIB_KFILE) \ +# ../libdialogpages/libdialogpages.la \ +# ../interfaces/libkompareinterface.la +# +## this is where the desktop file will go +#partdesktopdir = $(kde_servicesdir) +#partdesktop_DATA = komparepart.desktop +# +## this is where the part's XML-GUI resource file goes +#partrcdir = $(kde_datadir)/kompare +#partrc_DATA = komparepartui.rc +# diff --git a/kompare/komparepart/kompare_part.cpp b/kompare/komparepart/kompare_part.cpp new file mode 100644 index 00000000..08df1dc0 --- /dev/null +++ b/kompare/komparepart/kompare_part.cpp @@ -0,0 +1,956 @@ +/*************************************************************************** + kompare_part.cpp + ---------------- + begin : Sun Mar 4 2001 + Copyright 2001-2005,2009 Otto Bruggeman + Copyright 2001-2003 John Firebaugh + Copyright 2004 Jeff Snyder + Copyright 2007-2011 Kevin Kofler + Copyright 2012 Jean-Nicolas Artaud +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 "kompare_part.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 "diffmodel.h" +#include "komparelistview.h" +#include "kompareconnectwidget.h" +#include "diffsettings.h" +#include "viewsettings.h" +#include "kompareprefdlg.h" +#include "komparesaveoptionswidget.h" +#include "komparesplitter.h" +#include "kompareview.h" + +K_PLUGIN_FACTORY( KomparePartFactory, registerPlugin(); ) +K_EXPORT_PLUGIN( KomparePartFactory ) + +ViewSettings* KomparePart::m_viewSettings = 0L; +DiffSettings* KomparePart::m_diffSettings = 0L; + +KomparePart::KomparePart( QWidget *parentWidget, QObject *parent, const QVariantList & /*args*/ ) : + KParts::ReadWritePart(parent), + m_tempDiff( 0 ), + m_info() +{ + if( !m_viewSettings ) { + m_viewSettings = new ViewSettings( 0 ); + } + if( !m_diffSettings ) { + m_diffSettings = new DiffSettings( 0 ); + } + + readProperties( KGlobal::config().data() ); + + m_view = new KompareView ( m_viewSettings, parentWidget ); + setWidget( m_view ); + m_splitter = m_view->splitter(); + + // This creates the "Model creator" and connects the signals and slots + m_modelList = new Diff2::KompareModelList( m_diffSettings, m_splitter, this, "komparemodellist" , KParts::ReadWritePart::isReadWrite()); + + Q_FOREACH(QAction* action, m_modelList->actionCollection()->actions()) + { + actionCollection()->addAction(action->objectName(), action); + } + connect( m_modelList, SIGNAL(status( Kompare::Status )), + this, SLOT(slotSetStatus( Kompare::Status )) ); + connect( m_modelList, SIGNAL(setStatusBarModelInfo( int, int, int, int, int )), + this, SIGNAL(setStatusBarModelInfo( int, int, int, int, int )) ); + connect( m_modelList, SIGNAL(error( QString )), + this, SLOT(slotShowError( QString )) ); + connect( m_modelList, SIGNAL(applyAllDifferences( bool )), + this, SLOT(updateActions()) ); + connect( m_modelList, SIGNAL(applyDifference( bool )), + this, SLOT(updateActions()) ); + connect( m_modelList, SIGNAL(applyAllDifferences( bool )), + this, SIGNAL(appliedChanged()) ); + connect( m_modelList, SIGNAL(applyDifference( bool )), + this, SIGNAL(appliedChanged()) ); + connect( m_modelList, SIGNAL(updateActions()), this, SLOT(updateActions()) ); + + // This is the stuff to connect the "interface" of the kompare part to the model inside + connect( m_modelList, SIGNAL(modelsChanged(const Diff2::DiffModelList*)), + this, SIGNAL(modelsChanged(const Diff2::DiffModelList*)) ); + + connect( m_modelList, SIGNAL(setSelection(const Diff2::DiffModel*, const Diff2::Difference*)), + this, SIGNAL(setSelection(const Diff2::DiffModel*, const Diff2::Difference*)) ); + connect( this, SIGNAL(selectionChanged(const Diff2::DiffModel*, const Diff2::Difference*)), + m_modelList, SLOT(slotSelectionChanged(const Diff2::DiffModel*, const Diff2::Difference*)) ); + + connect( m_modelList, SIGNAL(setSelection(const Diff2::Difference*)), + this, SIGNAL(setSelection(const Diff2::Difference*)) ); + connect( this, SIGNAL(selectionChanged(const Diff2::Difference*)), + m_modelList, SLOT(slotSelectionChanged(const Diff2::Difference*)) ); + + connect( m_modelList, SIGNAL(applyDifference(bool)), + this, SIGNAL(applyDifference(bool)) ); + connect( m_modelList, SIGNAL(applyAllDifferences(bool)), + this, SIGNAL(applyAllDifferences(bool)) ); + connect( m_modelList, SIGNAL(applyDifference(const Diff2::Difference*, bool)), + this, SIGNAL(applyDifference(const Diff2::Difference*, bool)) ); + connect( m_modelList, SIGNAL(diffString(const QString&)), + this, SIGNAL(diffString(const QString&)) ); + + connect( this, SIGNAL(kompareInfo(Kompare::Info*)), m_modelList, SLOT(slotKompareInfo(Kompare::Info*)) ); + + // Here we connect the splitter to the modellist + connect( m_modelList, SIGNAL(setSelection(const Diff2::DiffModel*, const Diff2::Difference*)), + m_splitter, SLOT(slotSetSelection(const Diff2::DiffModel*, const Diff2::Difference*)) ); +// connect( m_splitter, SIGNAL(selectionChanged(const Diff2::Difference*, const Diff2::Difference*)), +// m_modelList, SLOT(slotSelectionChanged(const Diff2::Difference*, const Diff2::Difference*)) ); + connect( m_modelList, SIGNAL(setSelection(const Diff2::Difference*)), + m_splitter, SLOT(slotSetSelection(const Diff2::Difference*)) ); + connect( m_splitter, SIGNAL(selectionChanged(const Diff2::Difference*)), + m_modelList, SLOT(slotSelectionChanged(const Diff2::Difference*)) ); + + connect( m_modelList, SIGNAL(applyDifference(bool)), + m_splitter, SLOT(slotApplyDifference(bool)) ); + connect( m_modelList, SIGNAL(applyAllDifferences(bool)), + m_splitter, SLOT(slotApplyAllDifferences(bool)) ); + connect( m_modelList, SIGNAL(applyDifference(const Diff2::Difference*, bool)), + m_splitter, SLOT(slotApplyDifference(const Diff2::Difference*, bool)) ); + connect( this, SIGNAL(configChanged()), m_splitter, SIGNAL(configChanged()) ); + + setupActions(); + + // set our XML-UI resource file + setXMLFile( "komparepartui.rc" ); + + // we are read-write by default -> uhm what if we are opened by lets say konq in RO mode ? + // Then we should not be doing this... + setReadWrite( true ); + + // we are not modified since we haven't done anything yet + setModified( false ); +} + +KomparePart::~KomparePart() +{ + // This is the only place allowed to call cleanUpTemporaryFiles + // because before there might still be a use for them (when swapping) + cleanUpTemporaryFiles(); +} + +void KomparePart::setupActions() +{ + // create our actions + + m_saveAll = actionCollection()->addAction("file_save_all", this, SLOT(saveAll())); + m_saveAll->setIcon(KIcon("document-save-all")); + m_saveAll->setText(i18n("Save &All")); + m_saveDiff = actionCollection()->addAction("file_save_diff", this, SLOT(saveDiff())); + m_saveDiff->setText(i18n("Save &Diff...")); + m_swap = actionCollection()->addAction("file_swap", this, SLOT(slotSwap())); + m_swap->setText(i18n("Swap Source with Destination")); + m_diffStats = actionCollection()->addAction("file_diffstats", this, SLOT(slotShowDiffstats())); + m_diffStats->setText(i18n("Show Statistics")); + m_diffRefresh = actionCollection()->addAction("file_refreshdiff", this, SLOT(slotRefreshDiff())); + m_diffRefresh->setIcon(KIcon("view-refresh")); + m_diffRefresh->setText(i18n("Refresh Diff")); + m_diffRefresh->setShortcut(KStandardShortcut::reload()); + + m_print = actionCollection()->addAction(KStandardAction::Print, this, SLOT( slotFilePrint() )); + m_printPreview = actionCollection()->addAction(KStandardAction::PrintPreview, this, SLOT( slotFilePrintPreview() )); + KStandardAction::preferences(this, SLOT(optionsPreferences()), actionCollection()); +} + +void KomparePart::updateActions() +{ + m_saveAll->setEnabled ( m_modelList->hasUnsavedChanges() ); + m_saveDiff->setEnabled ( m_modelList->mode() == Kompare::ComparingFiles || m_modelList->mode() == Kompare::ComparingDirs ); + m_swap->setEnabled ( m_modelList->mode() == Kompare::ComparingFiles || m_modelList->mode() == Kompare::ComparingDirs ); + m_diffRefresh->setEnabled ( m_modelList->mode() == Kompare::ComparingFiles || m_modelList->mode() == Kompare::ComparingDirs ); + m_diffStats->setEnabled ( m_modelList->modelCount() > 0 ); + m_print->setEnabled ( m_modelList->modelCount() > 0 ); // If modellist has models then we have something to print, it's that simple. + m_printPreview->setEnabled( m_modelList ); +} + +void KomparePart::setEncoding( const QString& encoding ) +{ + kDebug(8103) << "Encoding: " << encoding << endl; + m_modelList->setEncoding( encoding ); +} + +bool KomparePart::openDiff( const KUrl& url ) +{ + kDebug(8103) << "Url = " << url.url() << endl; + + m_info.mode = Kompare::ShowingDiff; + m_info.source = url; + bool result = false; + fetchURL( url, true ); + + emit kompareInfo( &m_info ); + + if ( !m_info.localSource.isEmpty() ) + { + kDebug(8103) << "Download succeeded " << endl; + result = m_modelList->openDiff( m_info.localSource ); + updateActions(); + updateCaption(); + updateStatus(); + } + else + { + kDebug(8103) << "Download failed !" << endl; + } + + return result; +} + +bool KomparePart::openDiff( const QString& diffOutput ) +{ + bool value = false; + + m_info.mode = Kompare::ShowingDiff; + + emit kompareInfo( &m_info ); + + if ( m_modelList->parseAndOpenDiff( diffOutput ) == 0 ) + { + value = true; + updateActions(); + updateCaption(); + updateStatus(); + } + + return value; +} + +bool KomparePart::openDiff3( const KUrl& diff3Url ) +{ + // FIXME: Implement this !!! + kDebug(8103) << "Not implemented yet. Filename is: " << diff3Url.prettyUrl() << endl; + return false; +} + +bool KomparePart::openDiff3( const QString& diff3Output ) +{ + // FIXME: Implement this !!! + kDebug(8103) << "Not implemented yet. diff3 output is: " << endl; + kDebug(8103) << diff3Output << endl; + return false; +} + +bool KomparePart::exists( const QString& url ) +{ + QFileInfo fi( url ); + return fi.exists(); +} + +bool KomparePart::fetchURL( const KUrl& url, bool addToSource ) +{ + // Default value if there is an error is "", we rely on it! + QString tempFileName( "" ); + // Only in case of error do we set result to false, don't forget!! + bool result = true; + KTempDir* tmpDir = 0; + + if ( !url.isLocalFile() ) + { + KIO::UDSEntry node; + KIO::NetAccess::stat( url, node, widget() ); + if ( !node.isDir() ) + { + if ( ! KIO::NetAccess::download( url, tempFileName, widget() ) ) + { + slotShowError( i18n( "The URL %1 cannot be downloaded.", url.prettyUrl() ) ); + tempFileName = ""; // Not sure if download has already touched this tempFileName when there is an error + result = false; + } + } + else + { + tmpDir = new KTempDir(KStandardDirs::locateLocal("tmp", "kompare")); + tmpDir->setAutoRemove( true ); // Yes this is the default but just to make sure + if ( ! KIO::NetAccess::dircopy( url, KUrl( tmpDir->name() ), widget() ) ) + { + slotShowError( i18n( "The URL %1 cannot be downloaded.", url.prettyUrl() ) ); + delete tmpDir; + tmpDir = 0; + result = false; + } + else + { + tempFileName = tmpDir->name(); + kDebug(8101) << "tempFileName = " << tempFileName << endl; + // If a directory is copied into KTempDir then the directory in + // here is what I need to add to tempFileName + QDir dir( tempFileName ); + QStringList entries = dir.entryList( QDir::Dirs | QDir::NoDotAndDotDot ); + if ( entries.size() == 1 ) // More than 1 entry in here means big problems!!! + { + if ( !tempFileName.endsWith( '/' ) ) + tempFileName += '/'; + tempFileName += entries.at( 0 ); + tempFileName += '/'; + } + else + { + kDebug(8101) << "Yikes, nothing downloaded?" << endl; + delete tmpDir; + tmpDir = 0; + tempFileName = ""; + result = false; + } + } + } + } + else + { + // is Local already, check if exists + if ( exists( url.toLocalFile() ) ) + tempFileName = url.toLocalFile(); + else + { + slotShowError( i18n( "The URL %1 does not exist on your system.", url.prettyUrl() ) ); + result = false; + } + } + + if ( addToSource ) + { + m_info.localSource = tempFileName; + m_info.sourceKTempDir = tmpDir; + } + else + { + m_info.localDestination = tempFileName; + m_info.destinationKTempDir = tmpDir; + } + + return result; +} + +void KomparePart::cleanUpTemporaryFiles() +{ + kDebug(8101) << "Cleaning temporary files." << endl; + if ( !m_info.localSource.isEmpty() ) + { + if ( m_info.sourceKTempDir == 0 ) + KIO::NetAccess::removeTempFile( m_info.localSource ); + else + { + delete m_info.sourceKTempDir; + m_info.sourceKTempDir = 0; + } + m_info.localSource = ""; + } + if ( !m_info.localDestination.isEmpty() ) + { + if ( m_info.destinationKTempDir == 0 ) + KIO::NetAccess::removeTempFile( m_info.localDestination ); + else + { + delete m_info.destinationKTempDir; + m_info.destinationKTempDir = 0; + } + m_info.localDestination = ""; + } +} + +void KomparePart::compare( const KUrl& source, const KUrl& destination ) +{ + // FIXME: This is silly, i can use NetAccess::stat to figure out what it is and not + // wait until i am in the modellist to determine the mode we're supposed to be in. + // That should make the code more readable + // I should store the KTempDir(s)/File(s) in the Info struct as well and delete it at the right time + m_info.source = source; + m_info.destination = destination; + + // FIXME: (Not urgent) But turn this into an enum, for now i cant find a nice name for the enum that has Source and Destination as values + // For now we do not do error checking, user has already been notified and if the localString is empty then we dont diff + fetchURL( source, true ); + fetchURL( destination, false ); + + emit kompareInfo( &m_info ); + + compareAndUpdateAll(); +} + +void KomparePart::compareFileString( const KUrl & sourceFile, const QString & destination) +{ + //Set the modeto specify that the source is a file, and the destination is a string + m_info.mode = Kompare::ComparingFileString; + + m_info.source = sourceFile; + m_info.localDestination = destination; + + fetchURL(sourceFile, true); + + emit kompareInfo( &m_info ); + + compareAndUpdateAll(); +} + +void KomparePart::compareStringFile( const QString & source, const KUrl & destinationFile) +{ + //Set the modeto specify that the source is a file, and the destination is a string + m_info.mode = Kompare::ComparingStringFile; + + m_info.localSource = source; + m_info.destination = destinationFile; + + fetchURL(destinationFile, false); + + emit kompareInfo( &m_info ); + + compareAndUpdateAll(); +} + +void KomparePart::compareFiles( const KUrl& sourceFile, const KUrl& destinationFile ) +{ + m_info.mode = Kompare::ComparingFiles; + + m_info.source = sourceFile; + m_info.destination = destinationFile; + + // FIXME: (Not urgent) But turn this into an enum, for now i cant find a nice name for the enum that has Source and Destination as values + // For now we do not do error checking, user has already been notified and if the localString is empty then we dont diff + fetchURL( sourceFile, true ); + fetchURL( destinationFile, false ); + + emit kompareInfo( &m_info ); + + compareAndUpdateAll(); +} + +void KomparePart::compareDirs( const KUrl& sourceDirectory, const KUrl& destinationDirectory ) +{ + m_info.mode = Kompare::ComparingDirs; + + m_info.source = sourceDirectory; + m_info.destination = destinationDirectory; + + fetchURL( sourceDirectory, true ); + fetchURL( destinationDirectory, false ); + + emit kompareInfo( &m_info ); + + compareAndUpdateAll(); +} + +void KomparePart::compare3Files( const KUrl& /*originalFile*/, const KUrl& /*changedFile1*/, const KUrl& /*changedFile2*/ ) +{ + // FIXME: actually implement this some day :) + updateActions(); + updateCaption(); + updateStatus(); +} + +void KomparePart::openFileAndDiff( const KUrl& file, const KUrl& diffFile ) +{ + m_info.source = file; + m_info.destination = diffFile; + + fetchURL( file, true ); + fetchURL( diffFile, false ); + m_info.mode = Kompare::BlendingFile; + + emit kompareInfo( &m_info ); + + compareAndUpdateAll(); +} + +void KomparePart::openDirAndDiff ( const KUrl& dir, const KUrl& diffFile ) +{ + m_info.source = dir; + m_info.destination = diffFile; + + fetchURL( dir, true ); + fetchURL( diffFile, false ); + m_info.mode = Kompare::BlendingDir; + + emit kompareInfo( &m_info ); + + if ( !m_info.localSource.isEmpty() && !m_info.localDestination.isEmpty() ) + { + m_modelList->openDirAndDiff(); + //Must this be in here? couldn't we use compareAndUpdateAll as well? + updateActions(); + updateCaption(); + updateStatus(); + } +} + +bool KomparePart::openFile() +{ + // This is called from openURL + // This is a little inefficient but i will do it anyway + openDiff( url() ); + return true; +} + +bool KomparePart::saveAll() +{ + bool result = m_modelList->saveAll(); + updateActions(); + updateCaption(); + updateStatus(); + return result; +} + +void KomparePart::saveDiff() +{ + KDialog dlg( widget() ); + dlg.setObjectName( "save_options" ); + dlg.setModal( true ); + dlg.setWindowTitle( i18n("Diff Options") ); + dlg.setButtons( KDialog::Ok|KDialog::Cancel ); + KompareSaveOptionsWidget* w = new KompareSaveOptionsWidget( + m_info.localSource, + m_info.localDestination, + m_diffSettings, &dlg ); + dlg.setMainWidget( w ); + dlg.setButtonGuiItem( KDialog::Ok, KStandardGuiItem::save() ); + + if( dlg.exec() ) { + w->saveOptions(); + KSharedConfig::Ptr config = componentData().config(); + saveProperties( config.data() ); + config->sync(); + + while ( 1 ) + { + KUrl url = KFileDialog::getSaveUrl( m_info.destination.url(), + i18n("*.diff *.dif *.patch|Patch Files"), widget(), i18n( "Save .diff" ) ); + if ( KIO::NetAccess::exists( url, KIO::NetAccess::DestinationSide, widget() ) ) + { + int result = KMessageBox::warningYesNoCancel( widget(), i18n("The file exists or is write-protected; do you want to overwrite it?"), i18n("File Exists"), KGuiItem(i18n("Overwrite")), KGuiItem(i18n("Do Not Overwrite")) ); + if ( result == KMessageBox::Cancel ) + { + break; + } + else if ( result == KMessageBox::No ) + { + continue; + } + else + { + kDebug(8103) << "URL = " << url.prettyUrl() << endl; + kDebug(8103) << "Directory = " << w->directory() << endl; + kDebug(8103) << "DiffSettings = " << m_diffSettings << endl; + + m_modelList->saveDiff( url.url(), w->directory(), m_diffSettings ); + break; + } + } + else + { + kDebug(8103) << "URL = " << url.prettyUrl() << endl; + kDebug(8103) << "Directory = " << w->directory() << endl; + kDebug(8103) << "DiffSettings = " << m_diffSettings << endl; + + m_modelList->saveDiff( url.url(), w->directory(), m_diffSettings ); + break; + } + } + } +} + +void KomparePart::slotFilePrint() +{ + QPrinter printer; + printer.setOrientation( QPrinter::Landscape ); + QPrintDialog* dlg = KdePrint::createPrintDialog( &printer, m_splitter ); + + if ( dlg->exec() == QDialog::Accepted ) + { + // do some printing in qprinter + slotPaintRequested( &printer ); + } + + delete dlg; +} + +void KomparePart::slotFilePrintPreview() +{ + QPrinter printer; + printer.setOrientation( QPrinter::Landscape ); + QPrintPreviewDialog dlg( &printer ); + + connect( &dlg, SIGNAL(paintRequested(QPrinter*)), this, SLOT(slotPaintRequested(QPrinter*)) ); + + dlg.exec(); +} + +void KomparePart::slotPaintRequested( QPrinter* printer ) +{ + kDebug(8103) << "Now paint something..." << endl; + QPainter p; + p.begin( printer ); + + QSize widgetWidth = m_view->size(); + kDebug(8103) << "printer.width() = " << printer->width() << endl; + kDebug(8103) << "widgetWidth.width() = " << widgetWidth.width() << endl; + qreal factor = ((qreal)printer->width())/((qreal)widgetWidth.width()); + + kDebug(8103) << "factor = " << factor << endl; + + p.scale( factor, factor ); + m_view->render( &p ); + + p.end(); + kDebug(8103) << "Done painting something..." << endl; +} + +KAboutData* KomparePart::createAboutData() +{ + KAboutData *about = new KAboutData("kompare", 0, ki18n("KomparePart"), "4.0"); + about->addAuthor(ki18n("John Firebaugh"), ki18n("Author"), "jfirebaugh@kde.org"); + about->addAuthor(ki18n("Otto Bruggeman"), ki18n("Author"), "bruggie@gmail.com" ); + about->addAuthor(ki18n("Kevin Kofler"), ki18n("Author"), "kevin.kofler@chello.at" ); + return about; +} + +void KomparePart::slotSetStatus( enum Kompare::Status status ) +{ + updateActions(); + + switch( status ) { + case Kompare::RunningDiff: + emit setStatusBarText( i18n( "Running diff..." ) ); + break; + case Kompare::Parsing: + emit setStatusBarText( i18n( "Parsing diff output..." ) ); + break; + case Kompare::FinishedParsing: + updateStatus(); + break; + case Kompare::FinishedWritingDiff: + updateStatus(); + emit diffURLChanged(); + break; + default: + break; + } +} + +void KomparePart::updateCaption() +{ + QString source = m_info.source.prettyUrl(); + QString destination = m_info.destination.prettyUrl(); + + QString text; + + switch ( m_info.mode ) + { + case Kompare::ComparingFiles : + case Kompare::ComparingDirs : + case Kompare::BlendingFile : + case Kompare::BlendingDir : + text = source + " -- " + destination; // no need to translate this " -- " + break; + case Kompare::ShowingDiff : + text = source; + break; + default: + break; + } + + emit setWindowCaption( text ); +} + +void KomparePart::updateStatus() +{ + QString source = m_info.source.prettyUrl(); + QString destination = m_info.destination.prettyUrl(); + + QString text; + + switch ( m_info.mode ) + { + case Kompare::ComparingFiles : + text = i18n( "Comparing file %1 with file %2" , + source , + destination ); + break; + case Kompare::ComparingDirs : + text = i18n( "Comparing files in %1 with files in %2" , + source , + destination ); + break; + case Kompare::ShowingDiff : + text = i18n( "Viewing diff output from %1", source ); + break; + case Kompare::BlendingFile : + text = i18n( "Blending diff output from %1 into file %2" , + source , + destination ); + break; + case Kompare::BlendingDir : + text = i18n( "Blending diff output from %1 into folder %2" , + m_info.source.prettyUrl() , + m_info.destination.prettyUrl() ); + break; + default: + break; + } + + emit setStatusBarText( text ); +} + +void KomparePart::compareAndUpdateAll() +{ + if ( !m_info.localSource.isEmpty() && !m_info.localDestination.isEmpty() ) + { + switch(m_info.mode) + { + default: + case Kompare::UnknownMode: + m_modelList->compare(); + break; + + case Kompare::ComparingStringFile: + case Kompare::ComparingFileString: + case Kompare::ComparingFiles: + case Kompare::ComparingDirs: + m_modelList->compare(m_info.mode); + break; + + case Kompare::BlendingFile: + m_modelList->openFileAndDiff(); + break; + } + updateCaption(); + updateStatus(); + } + updateActions(); +} + +void KomparePart::slotShowError( QString error ) +{ + KMessageBox::error( widget(), error ); +} + +void KomparePart::slotSwap() +{ + if ( m_modelList->hasUnsavedChanges() ) + { + int query = KMessageBox::warningYesNoCancel + ( + widget(), + i18n( "You have made changes to the destination file(s).\n" + "Would you like to save them?" ), + i18n( "Save Changes?" ), + KStandardGuiItem::save(), + KStandardGuiItem::discard() + ); + + if ( query == KMessageBox::Yes ) + m_modelList->saveAll(); + + if ( query == KMessageBox::Cancel ) + return; // Abort prematurely so no swapping + } + + // Swap the info in the Kompare::Info struct + m_info.swapSourceWithDestination(); + + // Update window caption and statusbar text + updateCaption(); + updateStatus(); + + m_modelList->swap(); +} + +void KomparePart::slotRefreshDiff() +{ + if ( m_modelList->hasUnsavedChanges() ) + { + int query = KMessageBox::warningYesNoCancel + ( + widget(), + i18n( "You have made changes to the destination file(s).\n" + "Would you like to save them?" ), + i18n( "Save Changes?" ), + KStandardGuiItem::save(), + KStandardGuiItem::discard() + ); + + if ( query == KMessageBox::Cancel ) + return; // Abort prematurely so no refreshing + + if ( query == KMessageBox::Yes ) + m_modelList->saveAll(); + } + + // For this to work properly you have to refetch the files from their (remote) locations + cleanUpTemporaryFiles(); + fetchURL( m_info.source, true ); + fetchURL( m_info.destination, false ); + m_modelList->refresh(); +} + +void KomparePart::slotShowDiffstats( void ) +{ + // Fetch all the args needed for komparestatsmessagebox + // oldfile, newfile, diffformat, noofhunks, noofdiffs + + QString oldFile; + QString newFile; + QString diffFormat; + int filesInDiff; + int noOfHunks; + int noOfDiffs; + + oldFile = m_modelList->selectedModel() ? m_modelList->selectedModel()->sourceFile() : QString( "" ); + newFile = m_modelList->selectedModel() ? m_modelList->selectedModel()->destinationFile() : QString( "" ); + + if ( m_modelList->selectedModel() ) + { + switch( m_info.format ) { + case Kompare::Unified : + diffFormat = i18n( "Unified" ); + break; + case Kompare::Context : + diffFormat = i18n( "Context" ); + break; + case Kompare::RCS : + diffFormat = i18n( "RCS" ); + break; + case Kompare::Ed : + diffFormat = i18n( "Ed" ); + break; + case Kompare::Normal : + diffFormat = i18n( "Normal" ); + break; + case Kompare::UnknownFormat : + default: + diffFormat = i18n( "Unknown" ); + break; + } + } + else + { + diffFormat = ""; + } + + filesInDiff = m_modelList->modelCount(); + + noOfHunks = m_modelList->selectedModel() ? m_modelList->selectedModel()->hunkCount() : 0; + noOfDiffs = m_modelList->selectedModel() ? m_modelList->selectedModel()->differenceCount() : 0; + + if ( m_modelList->modelCount() == 0 ) { // no diff loaded yet + KMessageBox::information( 0L, i18n( + "No diff file, or no 2 files have been diffed. " + "Therefore no stats are available."), + i18n("Diff Statistics"), QString(), 0 ); + } + else if ( m_modelList->modelCount() == 1 ) { // 1 file in diff, or 2 files compared + KMessageBox::information( 0L, i18n( + "Statistics:\n" + "\n" + "Old file: %1\n" + "New file: %2\n" + "\n" + "Format: %3\n" + "Number of hunks: %4\n" + "Number of differences: %5", + oldFile, newFile, diffFormat, + noOfHunks, noOfDiffs), + i18n("Diff Statistics"), QString(), 0 ); + } else { // more than 1 file in diff, or 2 directories compared + KMessageBox::information( 0L, ki18n( + "Statistics:\n" + "\n" + "Number of files in diff file: %1\n" + "Format: %2\n" + "\n" + "Current old file: %3\n" + "Current new file: %4\n" + "\n" + "Number of hunks: %5\n" + "Number of differences: %6") + .subs(filesInDiff).subs(diffFormat).subs(oldFile) + .subs(newFile).subs(noOfHunks).subs(noOfDiffs) + .toString(), + i18n("Diff Statistics"), QString(), 0 ); + } +} + +bool KomparePart::queryClose() +{ + if ( !m_modelList->hasUnsavedChanges() ) return true; + + int query = KMessageBox::warningYesNoCancel + ( + widget(), + i18n("You have made changes to the destination file(s).\n" + "Would you like to save them?" ), + i18n( "Save Changes?" ), + KStandardGuiItem::save(), + KStandardGuiItem::discard() + ); + + if( query == KMessageBox::Cancel ) + return false; + + if( query == KMessageBox::Yes ) + return m_modelList->saveAll(); + + return true; +} + +int KomparePart::readProperties( KConfig *config ) +{ + m_viewSettings->loadSettings( config ); + m_diffSettings->loadSettings( config ); + emit configChanged(); + return 0; +} + +int KomparePart::saveProperties( KConfig *config ) +{ + m_viewSettings->saveSettings( config ); + m_diffSettings->saveSettings( config ); + return 0; +} + +void KomparePart::optionsPreferences() +{ + // show preferences + KomparePrefDlg pref( m_viewSettings, m_diffSettings ); + + connect( &pref, SIGNAL(configChanged()), this, SIGNAL(configChanged()) ); + + if ( pref.exec() ) + emit configChanged(); +} + +#include "kompare_part.moc" diff --git a/kompare/komparepart/kompare_part.h b/kompare/komparepart/kompare_part.h new file mode 100644 index 00000000..0c4d3dd4 --- /dev/null +++ b/kompare/komparepart/kompare_part.h @@ -0,0 +1,241 @@ +/*************************************************************************** + kompare_part.h + -------------- + begin : Sun Mar 4 2001 + Copyright 2001-2005,2009 Otto Bruggeman + Copyright 2001-2003 John Firebaugh + Copyright 2004 Jeff Snyder + Copyright 2007-2011 Kevin Kofler +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 KOMPAREPART_H +#define KOMPAREPART_H + +#include +#include +#include +#include + +#include "kompareinterface.h" + +class QPrinter; +class QWidget; + +class KTemporaryFile; +class KUrl; +class KAboutData; +class KAction; + +namespace Diff2 { +class Difference; +class DiffModel; +class DiffModelList; +class KompareModelList; +} +class DiffSettings; +class ViewSettings; +class KompareSplitter; +class KompareView; + +/** + * This is a "Part". It does all the real work in a KPart + * application. + * + * @short Main Part + * @author John Firebaugh + * @author Otto Bruggeman + * @version 0.3 + */ +class KomparePart : public KParts::ReadWritePart, + public KompareInterface +{ + Q_OBJECT + Q_INTERFACES(KompareInterface) +public: + /** + * Default constructor + */ + KomparePart( QWidget *parentWidget, QObject *parent, const QVariantList & /*args*/); + + /** + * Destructor + */ + virtual ~KomparePart(); + + // Sessionmanagement stuff, added to the kompare iface + // because they are not in the Part class where they belong + // Should be added when bic changes are allowed again (kde 4.0) + virtual int readProperties( KConfig *config ); + virtual int saveProperties( KConfig *config ); + // this one is called when the shell_app is about to close. + // we need it now to save the properties of the part when apps don't (can't) + // use the readProperties and saveProperties methods + virtual bool queryClose(); + + // Do we really want to expose this ??? + const Diff2::KompareModelList* model() const { return m_modelList; }; + + static KAboutData *createAboutData(); + +public: + // Reimplemented from the KompareInterface + /** + * Open and parse the diff file at diffUrl. + */ + virtual bool openDiff( const KUrl& diffUrl ); + + /** Added on request of Harald Fernengel */ + virtual bool openDiff( const QString& diffOutput ); + + /** Open and parse the diff3 file at diff3Url */ + virtual bool openDiff3( const KUrl& diff3URL ); + + /** Open and parse the file diff3Output with the output of diff3 */ + virtual bool openDiff3( const QString& diff3Output ); + + /** Compare, with diff, source with destination */ + virtual void compare( const KUrl& sourceFile, const KUrl& destinationFile ); + + /** Compare a Source file to a custom Destination string */ + virtual void compareFileString( const KUrl & sourceFile, const QString & destination); + + /** Compare a custom Source string to a Destination file */ + virtual void compareStringFile( const QString & source, const KUrl & destinationFile); + + /** Compare, with diff, source with destination */ + virtual void compareFiles( const KUrl& sourceFile, const KUrl& destinationFile ); + + /** Compare, with diff, source with destination */ + virtual void compareDirs ( const KUrl& sourceDir, const KUrl& destinationDir ); + + /** Compare, with diff3, originalFile with changedFile1 and changedFile2 */ + virtual void compare3Files( const KUrl& originalFile, const KUrl& changedFile1, const KUrl& changedFile2 ); + + /** This will show the file and the file with the diff applied */ + virtual void openFileAndDiff( const KUrl& file, const KUrl& diffFile ); + + /** This will show the directory and the directory with the diff applied */ + virtual void openDirAndDiff ( const KUrl& dir, const KUrl& diffFile ); + + /** Reimplementing this because this one knows more about the real part then the interface */ + virtual void setEncoding( const QString& encoding ); + + // This is the interpart interface, it is signal and slot based so no "real" interface here + // All you have to do is connect the parts from your application. + // These just point to their counterpart in the KompareModelList or get called from their + // counterpart in KompareModelList. +signals: + void modelsChanged( const Diff2::DiffModelList* models ); + + void setSelection( const Diff2::DiffModel* model, const Diff2::Difference* diff ); + void setSelection( const Diff2::Difference* diff ); + + void selectionChanged( const Diff2::DiffModel* model, const Diff2::Difference* diff ); + void selectionChanged( const Diff2::Difference* diff ); + + void applyDifference( bool apply ); + void applyAllDifferences( bool apply ); + void applyDifference( const Diff2::Difference*, bool apply ); + + void configChanged(); + + /* + ** This is emitted when a difference is clicked in the kompare view. You can connect to + ** it so you can use it to jump to this particular line in the editor in your app. + */ + void differenceClicked( int lineNumber ); + + // Stuff that can probably be removed by putting it in the part where it belongs in my opinion +public slots: + /** Save all destinations. */ + bool saveAll(); + + /** Save the results of a comparison as a diff file. */ + void saveDiff(); + + /** To enable printing, the part has the only interesting printable content so putting it here */ + void slotFilePrint(); + void slotFilePrintPreview(); + +signals: + void appliedChanged(); + void diffURLChanged(); + void kompareInfo( Kompare::Info* info ); + void setStatusBarModelInfo( int modelIndex, int differenceIndex, int modelCount, int differenceCount, int appliedCount ); +// void setStatusBarText( const QString& text ); + void diffString(const QString&); + +protected: + /** + * This is the method that gets called when the file is opened, + * when using openURL( const KUrl& ) or in our case also openDiff( const KUrl& ); + * return true when everything went ok, false if there were problems + */ + virtual bool openFile(); + // ... Uhm we return true without saving ??? + virtual bool saveFile() { return true; }; + + // patchFile + bool patchFile(KUrl&); + bool patchDir(); + +protected slots: + void slotSetStatus( Kompare::Status status ); + void slotShowError( QString error ); + + void slotSwap(); + void slotShowDiffstats(); + void slotRefreshDiff(); + void optionsPreferences(); + + void updateActions(); + void updateCaption(); + void updateStatus(); + void compareAndUpdateAll(); + + void slotPaintRequested( QPrinter* ); + +private: + void cleanUpTemporaryFiles(); + void setupActions(); + bool exists( const QString& url ); + bool isDirectory( const KUrl& url ); + // FIXME (like in cpp file not urgent) Replace with enum, cant find a proper + // name now but it is private anyway so can not be used from outside + bool fetchURL( const KUrl& url, bool isSource ); + +private: + // Uhm why were these static again ??? + // Ah yes, so multiple instances of kompare use the + // same settings after one of them changes them + static ViewSettings* m_viewSettings; + static DiffSettings* m_diffSettings; + + Diff2::KompareModelList* m_modelList; + + KompareView* m_view; + KompareSplitter* m_splitter; + + KAction* m_saveAll; + KAction* m_saveDiff; + KAction* m_swap; + KAction* m_diffStats; + KAction* m_diffRefresh; + KAction* m_print; + KAction* m_printPreview; + + KTemporaryFile* m_tempDiff; + + struct Kompare::Info m_info; +}; + +#endif // KOMPAREPART_H diff --git a/kompare/komparepart/kompareconnectwidget.cpp b/kompare/komparepart/kompareconnectwidget.cpp new file mode 100644 index 00000000..2a8cb920 --- /dev/null +++ b/kompare/komparepart/kompareconnectwidget.cpp @@ -0,0 +1,258 @@ +/*************************************************************************** + kompareconnectwidget.cpp + ------------------------ + begin : Tue Jun 26 2001 + Copyright 2001-2003 John Firebaugh + Copyright 2001-2009 Otto Bruggeman + Copyright 2004 Jeff Snyder + Copyright 2007-2011 Kevin Kofler + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the 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 "kompareconnectwidget.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "viewsettings.h" +#include "komparelistview.h" +#include "komparesplitter.h" + +using namespace Diff2; + +KompareConnectWidgetFrame::KompareConnectWidgetFrame( ViewSettings* settings, + KompareSplitter* parent, + const char* name ) : + QSplitterHandle(Qt::Horizontal, (QSplitter *)parent), + m_wid ( settings, this, name ), + m_label ( " ", this ), // putting a space here because Qt 4 computes different size hints for empty labels + m_layout ( this ) +{ + setObjectName( name ); + setSizePolicy ( QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Ignored) ); + m_wid.setSizePolicy ( QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored) ); + m_label.setSizePolicy ( QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed) ); + m_label.setMargin(3); + QFrame* bottomLine = new QFrame(this); + bottomLine->setFrameShape(QFrame::HLine); + bottomLine->setFrameShadow ( QFrame::Plain ); + bottomLine->setSizePolicy ( QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed) ); + bottomLine->setFixedHeight(1); + m_layout.setSpacing(0); + m_layout.setMargin(0); + m_layout.addWidget(&m_label); + m_layout.addWidget(bottomLine); + m_layout.addWidget(&m_wid); +} + +KompareConnectWidgetFrame::~KompareConnectWidgetFrame() +{ +} + +QSize KompareConnectWidgetFrame::sizeHint() const +{ + return QSize(50, style()->pixelMetric( QStyle::PM_ScrollBarExtent ) ); +} + +#if 0 +static int kMouseOffset; + +void KompareConnectWidgetFrame::mouseMoveEvent( QMouseEvent *e ) +{ + + if ( !(e->state()&Qt::LeftButton) ) + return; + + QCOORD pos = s->pick( parentWidget()->mapFromGlobal(e->globalPos()) ) + - kMouseOffset; + + ((KompareSplitter*)s)->moveSplitter( pos, id() ); +} + +void KompareConnectWidgetFrame::mousePressEvent( QMouseEvent *e ) +{ + if ( e->button() == Qt::LeftButton ) + kMouseOffset = s->pick( e->pos() ); + QSplitterHandle::mousePressEvent(e); +} + +void KompareConnectWidgetFrame::mouseReleaseEvent( QMouseEvent *e ) +{ + if ( !opaque() && e->button() == Qt::LeftButton ) { + QCOORD pos = s->pick( parentWidget()->mapFromGlobal(e->globalPos()) ) + - kMouseOffset; + ((KompareSplitter*)s)->moveSplitter( pos, id() ); + } +} +#endif + +KompareConnectWidget::KompareConnectWidget( ViewSettings* settings, QWidget* parent, const char* name ) + : QWidget(parent), + m_settings( settings ), + m_selectedModel( 0 ), + m_selectedDifference( 0 ) +{ + setObjectName(name); +// connect( m_settings, SIGNAL( settingsChanged() ), this, SLOT( slotDelayedRepaint() ) ); + setAttribute( Qt::WA_NoSystemBackground, true ); + setSizePolicy( QSizePolicy( QSizePolicy::Fixed, QSizePolicy::Minimum ) ); + setFocusProxy( parent->parentWidget() ); +} + +KompareConnectWidget::~KompareConnectWidget() +{ + m_settings = 0; + m_selectedModel = 0; + m_selectedDifference = 0; +} + +void KompareConnectWidget::slotSetSelection( const DiffModel* model, const Difference* diff ) +{ + if( m_selectedModel == model && m_selectedDifference == diff ) + return; + + if ( m_selectedModel == model && m_selectedDifference != diff ) + { + m_selectedDifference = diff; + slotDelayedRepaint(); + return; + } + + m_selectedModel = model; + m_selectedDifference = diff; + + slotDelayedRepaint(); +} + +void KompareConnectWidget::slotDelayedRepaint() +{ + QTimer::singleShot( 0, this, SLOT( repaint() ) ); +} + +void KompareConnectWidget::slotSetSelection( const Difference* diff ) +{ + if ( m_selectedDifference == diff ) + return; + + m_selectedDifference = diff; + + slotDelayedRepaint(); +} + +void KompareConnectWidget::paintEvent( QPaintEvent* /* e */ ) +{ + QPixmap pixbuf(size()); + QPainter paint(&pixbuf); + QPainter* p = &paint; + + p->setRenderHint(QPainter::Antialiasing); + p->fillRect( 0, 0, pixbuf.width(), pixbuf.height(), palette().color( QPalette::Window ) ); + p->translate(QPointF(0, 0.5)); + + KompareSplitter* splitter = static_cast( parent()->parent() ); + int count = splitter->count(); + KompareListView *leftView = count >= 2 ? static_cast( splitter->widget(0) )->view() : 0; + KompareListView *rightView = count >= 2 ? static_cast( splitter->widget(1) )->view() : 0; + + if ( m_selectedModel && leftView && rightView ) + { + int firstL = leftView->firstVisibleDifference(); + int firstR = rightView->firstVisibleDifference(); + int lastL = leftView->lastVisibleDifference(); + int lastR = rightView->lastVisibleDifference(); + + int first = firstL < 0 ? firstR : qMin( firstL, firstR ); + int last = lastL < 0 ? lastR : qMax( lastL, lastR ); +// kDebug(8106) << " left: " << firstL << " - " << lastL << endl; +// kDebug(8106) << " right: " << firstR << " - " << lastR << endl; +// kDebug(8106) << " drawing: " << first << " - " << last << endl; + if ( first >= 0 && last >= 0 && first <= last ) + { + const DifferenceList* differences = const_cast(m_selectedModel)->differences(); + QRect leftRect, rightRect; + for ( int i = first; i <= last; ++i ) + { + Difference* diff = differences->at(i ); + bool selected = ( diff == m_selectedDifference ); + + if ( QApplication::isRightToLeft() ) + { + leftRect = rightView->itemRect( i ); + rightRect = leftView->itemRect( i ); + } + else + { + leftRect = leftView->itemRect( i ); + rightRect = rightView->itemRect( i ); + } + + int tl = leftRect.top(); + int tr = rightRect.top(); + int bl = leftRect.bottom(); + int br = rightRect.bottom(); + + // Bah, stupid 16-bit signed shorts in that crappy X stuff... + tl = tl >= -32768 ? tl : -32768; + tr = tr >= -32768 ? tr : -32768; + bl = bl <= 32767 ? bl : 32767; + br = br <= 32767 ? br : 32767; + + QPainterPath topBezier = makeBezier( tl, tr ); + QPainterPath bottomBezier = makeBezier( bl, br ); + + QPainterPath poly(topBezier); + poly.connectPath(bottomBezier.toReversed()); + poly.closeSubpath(); + + QColor bg = m_settings->colorForDifferenceType( diff->type(), selected, diff->applied() ); + p->setPen( bg ); + p->setBrush( bg ); + p->drawPath(poly); + + if(selected) + { + p->setPen( bg.dark( 135 ) ); + p->setBrush( Qt::NoBrush ); + p->drawPath( topBezier ); + p->drawPath( bottomBezier.toReversed() ); + } + } + } + } + +// p->flush(); + QPainter widgetPainter(this); + widgetPainter.drawImage(0, 0, pixbuf.toImage()); +} + +QPainterPath KompareConnectWidget::makeBezier( int leftHeight, int rightHeight ) const +{ + int r = width(); + int o = (int)((double)r*0.4); // 40% of width + + QPainterPath p(QPointF(0, leftHeight)); + if(leftHeight==rightHeight) { + p.lineTo(QPointF(r, rightHeight)); + } else { + p.cubicTo(QPointF(o, leftHeight), QPointF(r-o, rightHeight), QPointF(r,rightHeight)); + } + return p; +} + +#include "kompareconnectwidget.moc" diff --git a/kompare/komparepart/kompareconnectwidget.h b/kompare/komparepart/kompareconnectwidget.h new file mode 100644 index 00000000..03eb746c --- /dev/null +++ b/kompare/komparepart/kompareconnectwidget.h @@ -0,0 +1,93 @@ +/*************************************************************************** + kompareconnectwidget.h + ---------------------- + begin : Tue Jun 26 2001 + Copyright 2001-2003 John Firebaugh + Copyright 2001-2004 Otto Bruggeman + Copyright 2004 Jeff Snyder + Copyright 2007 Kevin Kofler + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the 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 KOMPARECONNECTWIDGET_H +#define KOMPARECONNECTWIDGET_H + +#include +#include +#include +#include +#include +#include + +#include "komparemodellist.h" + +namespace Diff2 { +class DiffModel; +} +class ViewSettings; +class KompareSplitter; + +class KompareConnectWidget : public QWidget +{ + Q_OBJECT + +public: + KompareConnectWidget( ViewSettings* settings, QWidget* parent, const char* name = 0 ); + ~KompareConnectWidget(); + +public slots: + void slotSetSelection( const Diff2::DiffModel* model, const Diff2::Difference* diff ); + void slotSetSelection( const Diff2::Difference* diff ); + + void slotDelayedRepaint(); + +signals: + void selectionChanged(const Diff2::Difference* diff); + +protected: + void paintEvent( QPaintEvent* e ); + QPainterPath makeBezier( int l, int r ) const; + +private: + ViewSettings* m_settings; + + const Diff2::DiffModel* m_selectedModel; + const Diff2::Difference* m_selectedDifference; +}; + +class KompareConnectWidgetFrame : public QSplitterHandle +{ + Q_OBJECT +public: + KompareConnectWidgetFrame( ViewSettings* settings, KompareSplitter* parent, const char* name = 0 ); + ~KompareConnectWidgetFrame(); + + QSize sizeHint() const; + + KompareConnectWidget* wid() { return &m_wid; } + +protected: + // stop the parent QSplitterHandle painting + void paintEvent( QPaintEvent* /* e */ ) { } + +#if 0 + void mouseMoveEvent( QMouseEvent * ); + void mousePressEvent( QMouseEvent * ); + void mouseReleaseEvent( QMouseEvent * ); +#endif + +private: + KompareConnectWidget m_wid; + QLabel m_label; + QVBoxLayout m_layout; +}; + +#endif diff --git a/kompare/komparepart/komparelistview.cpp b/kompare/komparepart/komparelistview.cpp new file mode 100644 index 00000000..b2935c91 --- /dev/null +++ b/kompare/komparepart/komparelistview.cpp @@ -0,0 +1,974 @@ +/*************************************************************************** + komparelistview.h + ----------------- + begin : Sun Mar 4 2001 + Copyright 2001-2009 Otto Bruggeman + Copyright 2001-2003 John Firebaugh + Copyright 2004 Jeff Snyder + Copyright 2007-2012 Kevin Kofler +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 "komparelistview.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "diffmodel.h" +#include "diffhunk.h" +#include "difference.h" +#include "viewsettings.h" +#include "komparemodellist.h" +#include "komparesplitter.h" + +#define COL_LINE_NO 0 +#define COL_MAIN 1 + +#define BLANK_LINE_HEIGHT 3 +#define HUNK_LINE_HEIGHT 5 + +#define ITEM_MARGIN 3 + +using namespace Diff2; + +KompareListViewFrame::KompareListViewFrame( bool isSource, + ViewSettings* settings, + KompareSplitter* parent, + const char* name ): + QFrame ( parent ), + m_view ( isSource, settings, this, name ), + m_label ( isSource?"Source":"Dest", this ), + m_layout ( this ) +{ + setSizePolicy ( QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored) ); + m_label.setSizePolicy ( QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed) ); + QFrame *bottomLine = new QFrame(this); + bottomLine->setFrameShape(QFrame::HLine); + bottomLine->setFrameShadow ( QFrame::Plain ); + bottomLine->setSizePolicy ( QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed) ); + bottomLine->setFixedHeight(1); + m_label.setMargin(3); + m_layout.setSpacing(0); + m_layout.setMargin(0); + m_layout.addWidget(&m_label); + m_layout.addWidget(bottomLine); + m_layout.addWidget(&m_view); + + connect( &m_view, SIGNAL(differenceClicked(const Diff2::Difference*)), + parent, SLOT(slotDifferenceClicked(const Diff2::Difference*)) ); + + connect( parent, SIGNAL(scrollViewsToId(int)), &m_view, SLOT(scrollToId(int)) ); + connect( parent, SIGNAL(setXOffset(int)), &m_view, SLOT(setXOffset(int)) ); + connect( &m_view, SIGNAL(resized()), parent, SLOT(slotUpdateScrollBars()) ); +} + +void KompareListViewFrame::slotSetModel( const DiffModel* model ) +{ + if( model ) + { + if( view()->isSource() ) { + if( !model->sourceRevision().isEmpty() ) + m_label.setText( model->sourceFile() + " (" + model->sourceRevision() + ')' ); + else + m_label.setText( model->sourceFile() ); + } else { + if( !model->destinationRevision().isEmpty() ) + m_label.setText( model->destinationFile() + " (" + model->destinationRevision() + ')' ); + else + m_label.setText( model->destinationFile() ); + } + } else { + m_label.setText( QString::null ); //krazy:exclude=nullstrassign for old broken gcc + } +} + +KompareListView::KompareListView( bool isSource, + ViewSettings* settings, + QWidget* parent, const char* name ) : + QTreeWidget( parent ), + m_isSource( isSource ), + m_settings( settings ), + m_scrollId( -1 ), + m_selectedModel( 0 ), + m_selectedDifference( 0 ) +{ + setObjectName( name ); + setItemDelegate( new KompareListViewItemDelegate( this ) ); + setHeaderHidden( true ); + setColumnCount( 3 ); // Line Number, Main, Blank + setAllColumnsShowFocus( true ); + setRootIsDecorated( false ); + setIndentation( 0 ); + setFrameStyle( QFrame::NoFrame ); + setVerticalScrollMode( QAbstractItemView::ScrollPerPixel ); + setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); + setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); + setFocusPolicy( Qt::NoFocus ); + setFont( m_settings->m_font ); + setFocusProxy( parent->parentWidget() ); +} + +KompareListView::~KompareListView() +{ + m_settings = 0; + m_selectedModel = 0; + m_selectedDifference = 0; +} + +KompareListViewItem* KompareListView::itemAtIndex( int i ) +{ + return m_items[ i ]; +} + +int KompareListView::firstVisibleDifference() +{ + QTreeWidgetItem* item = itemAt( QPoint( 0, 0 ) ); + + if( item == 0 ) + { + kDebug(8104) << "no item at viewport coordinates (0,0)" << endl; + } + + while( item ) { + KompareListViewLineItem* lineItem = dynamic_cast(item); + if( lineItem && lineItem->diffItemParent()->difference()->type() != Difference::Unchanged ) + break; + item = itemBelow(item); + } + + if( item ) + return m_items.indexOf( ((KompareListViewLineItem*)item)->diffItemParent() ); + + return -1; +} + +int KompareListView::lastVisibleDifference() +{ + QTreeWidgetItem* item = itemAt( QPoint( 0, visibleHeight() - 1 ) ); + + if( item == 0 ) + { + kDebug(8104) << "no item at viewport coordinates (0," << visibleHeight() - 1 << ")" << endl; + // find last item + item = itemAt( QPoint( 0, 0 ) ); + if( item ) { + QTreeWidgetItem* nextItem = item; + do { + item = nextItem; + nextItem = itemBelow( item ); + } while( nextItem ); + } + } + + while( item ) { + KompareListViewLineItem* lineItem = dynamic_cast(item); + if( lineItem && lineItem->diffItemParent()->difference()->type() != Difference::Unchanged ) + break; + item = itemAbove(item); + } + + if( item ) + return m_items.indexOf( ((KompareListViewLineItem*)item)->diffItemParent() ); + + return -1; +} + +QRect KompareListView::totalVisualItemRect( QTreeWidgetItem* item ) +{ + QRect total = visualItemRect( item ); + int n = item->childCount(); + for( int i=0; ichild( i ); + if( !child->isHidden() ) + total = total.united( totalVisualItemRect( child ) ); + } + return total; +} + +QRect KompareListView::itemRect( int i ) +{ + QTreeWidgetItem* item = itemAtIndex( i ); + return totalVisualItemRect( item ); +} + +int KompareListView::minScrollId() +{ + return visibleHeight() / 2; +} + +int KompareListView::maxScrollId() +{ + int n = topLevelItemCount(); + if(!n) return 0; + KompareListViewItem* item = (KompareListViewItem*)topLevelItem( n-1 ); + int maxId = item->scrollId() + item->maxHeight() - minScrollId(); + kDebug(8104) << "Max ID = " << maxId << endl; + return maxId; +} + +int KompareListView::contentsHeight() +{ + return verticalScrollBar()->maximum() + viewport()->height() - style()->pixelMetric( QStyle::PM_ScrollBarExtent ); +} + +int KompareListView::contentsWidth() +{ + return ( columnWidth(COL_LINE_NO) + columnWidth(COL_MAIN) ); +} + +int KompareListView::visibleHeight() +{ + return viewport()->height(); +} + +int KompareListView::visibleWidth() +{ + return viewport()->width(); +} + +int KompareListView::contentsX() +{ + return horizontalOffset(); +} + +int KompareListView::contentsY() +{ + return verticalOffset(); +} + +int KompareListView::nextPaintOffset() const +{ + return m_nextPaintOffset; +} + +void KompareListView::setNextPaintOffset(int offset) +{ + m_nextPaintOffset = offset; +} + +void KompareListView::setXOffset( int x ) +{ + kDebug(8104) << "SetXOffset : Scroll to x position: " << x << endl; + horizontalScrollBar()->setValue( x ); +} + +void KompareListView::scrollToId( int id ) +{ +// kDebug(8104) << "ScrollToID : Scroll to id : " << id << endl; + int n = topLevelItemCount(); + KompareListViewItem* item = 0; + if( n ) { + int i = 1; + for( ; iscrollId() > id ) + break; + } + item = (KompareListViewItem*)topLevelItem( i-1 ); + } + + if( item ) { + QRect rect = totalVisualItemRect( item ); + int pos = rect.top() + verticalOffset(); + int itemId = item->scrollId(); + int height = rect.height(); + double r = (double)( id - itemId ) / (double)item->maxHeight(); + int y = pos + (int)( r * (double)height ) - minScrollId(); +// kDebug(8104) << "scrollToID: " << endl; +// kDebug(8104) << " id = " << id << endl; +// kDebug(8104) << " pos = " << pos << endl; +// kDebug(8104) << " itemId = " << itemId << endl; +// kDebug(8104) << " r = " << r << endl; +// kDebug(8104) << " height = " << height << endl; +// kDebug(8104) << " minID = " << minScrollId() << endl; +// kDebug(8104) << " y = " << y << endl; +// kDebug(8104) << "contentsHeight = " << contentsHeight() << endl; +// kDebug(8104) << " c - y = " << contentsHeight() - y << endl; + verticalScrollBar()->setValue( y ); + } + + m_scrollId = id; +} + +int KompareListView::scrollId() +{ + if( m_scrollId < 0 ) + m_scrollId = minScrollId(); + return m_scrollId; +} + +void KompareListView::setSelectedDifference( const Difference* diff, bool scroll ) +{ + kDebug(8104) << "KompareListView::setSelectedDifference(" << diff << ", " << scroll << ")" << endl; + + // When something other than a click causes this function to be called, + // it'll only get called once, and all is simple. + // + // When the user clicks on a diff, this function will get called once when + // komparesplitter::slotDifferenceClicked runs, and again when the + // setSelection signal from the modelcontroller arrives. + // + // the first call (which will always be from the splitter) will have + // scroll==false, and the second call will bail out here. + // Which is why clicking on a difference does not cause the listviews to + // scroll. + if ( m_selectedDifference == diff ) + return; + + m_selectedDifference = diff; + + KompareListViewItem* item = m_itemDict[ diff ]; + if( !item ) { + kDebug(8104) << "KompareListView::slotSetSelection(): couldn't find our selection!" << endl; + return; + } + + // why does this not happen when the user clicks on a diff? see the comment above. + if( scroll ) + scrollToId(item->scrollId()); + setUpdatesEnabled( false ); + int x = horizontalScrollBar()->value(); + int y = verticalScrollBar()->value(); + setCurrentItem( item ); + horizontalScrollBar()->setValue( x ); + verticalScrollBar()->setValue( y ); + setUpdatesEnabled( true ); +} + +void KompareListView::slotSetSelection( const Difference* diff ) +{ + kDebug(8104) << "KompareListView::slotSetSelection( const Difference* diff )" << endl; + + setSelectedDifference( diff, true ); +} + +void KompareListView::slotSetSelection( const DiffModel* model, const Difference* diff ) +{ + kDebug(8104) << "KompareListView::slotSetSelection( const DiffModel* model, const Difference* diff )" << endl; + + if( m_selectedModel && m_selectedModel == model ) { + slotSetSelection( diff ); + return; + } + + clear(); + m_items.clear(); + m_itemDict.clear(); + m_selectedModel = model; + + DiffHunkListConstIterator hunkIt = model->hunks()->begin(); + DiffHunkListConstIterator hEnd = model->hunks()->end(); + + KompareListViewItem* item = 0; + m_nextPaintOffset = 0; + + for ( ; hunkIt != hEnd; ++hunkIt ) + { + if( item ) + item = new KompareListViewHunkItem( this, item, *hunkIt, model->isBlended() ); + else + item = new KompareListViewHunkItem( this, *hunkIt, model->isBlended() ); + + DifferenceListConstIterator diffIt = (*hunkIt)->differences().begin(); + DifferenceListConstIterator dEnd = (*hunkIt)->differences().end(); + + for ( ; diffIt != dEnd; ++diffIt ) + { + item = new KompareListViewDiffItem( this, item, *diffIt ); + + int type = (*diffIt)->type(); + + if ( type != Difference::Unchanged ) + { + m_items.append( (KompareListViewDiffItem*)item ); + m_itemDict.insert( *diffIt, (KompareListViewDiffItem*)item ); + } + } + } + + resizeColumnToContents( COL_LINE_NO ); + resizeColumnToContents( COL_MAIN ); + + slotSetSelection( diff ); +} + +KompareListViewDiffItem* KompareListView::diffItemAt( const QPoint& pos ) +{ + KompareListViewItem* item = static_cast( itemAt( pos ) ); + if( !item ) + return 0; + switch( item->type() ) { + case KompareListViewItem::Hunk: + if( item->paintHeight() ) return 0; // no diff item here + // zero height (fake 1 pixel height), so a diff item shines through + return static_cast( itemBelow( item ) ); + case KompareListViewItem::Line: + case KompareListViewItem::Blank: + return static_cast( item )->diffItemParent(); + case KompareListViewItem::Container: + return static_cast( item )->diffItemParent(); + case KompareListViewItem::Diff: + return static_cast( item ); + default: + return 0; + } +} + +void KompareListView::mousePressEvent( QMouseEvent* e ) +{ + QPoint vp = e->pos(); + KompareListViewDiffItem* diffItem = diffItemAt( vp ); + if( diffItem && diffItem->difference()->type() != Difference::Unchanged ) { + emit differenceClicked( diffItem->difference() ); + } +} + +void KompareListView::mouseDoubleClickEvent( QMouseEvent* e ) +{ + QPoint vp = e->pos(); + KompareListViewDiffItem* diffItem = diffItemAt( vp ); + if ( diffItem && diffItem->difference()->type() != Difference::Unchanged ) { + // FIXME: make a new signal that does both + emit differenceClicked( diffItem->difference() ); + emit applyDifference( !diffItem->difference()->applied() ); + } +} + +void KompareListView::renumberLines( void ) +{ +// kDebug( 8104 ) << "Begin" << endl; + unsigned int newLineNo = 1; + if( !topLevelItemCount() ) return; + KompareListViewItem* item = (KompareListViewItem*)topLevelItem( 0 ); + while( item ) { +// kDebug( 8104 ) << "type: " << item->type() << endl; + if ( item->type() != KompareListViewItem::Container + && item->type() != KompareListViewItem::Blank + && item->type() != KompareListViewItem::Hunk ) + { +// kDebug( 8104 ) << QString::number( newLineNo ) << endl; + item->setText( COL_LINE_NO, QString::number( newLineNo++ ) ); + } + item = (KompareListViewItem*)itemBelow( item ); + } +} + +void KompareListView::slotApplyDifference( bool apply ) +{ + m_itemDict[ m_selectedDifference ]->applyDifference( apply ); + // now renumber the line column if this is the destination + if ( !m_isSource ) + renumberLines(); +} + +void KompareListView::slotApplyAllDifferences( bool apply ) +{ + QHash::ConstIterator it = m_itemDict.constBegin(); + QHash::ConstIterator end = m_itemDict.constEnd(); + for ( ; it != end; ++it ) + it.value()->applyDifference( apply ); + + // now renumber the line column if this is the destination + if ( !m_isSource ) + renumberLines(); + update(); +} + +void KompareListView::slotApplyDifference( const Difference* diff, bool apply ) +{ + m_itemDict[ diff ]->applyDifference( apply ); + // now renumber the line column if this is the destination + if ( !m_isSource ) + renumberLines(); +} + +void KompareListView::wheelEvent( QWheelEvent* e ) +{ + e->ignore(); // we want the parent to catch wheel events +} + +void KompareListView::resizeEvent( QResizeEvent* e ) +{ + QTreeWidget::resizeEvent(e); + emit resized(); +} + +KompareListViewItemDelegate::KompareListViewItemDelegate( QObject* parent ) + : QStyledItemDelegate( parent ) +{ +} + +KompareListViewItemDelegate::~KompareListViewItemDelegate() +{ +} + +void KompareListViewItemDelegate::paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const +{ + int column = index.column(); + QStyleOptionViewItemV4 changedOption = option; + if( column == COL_LINE_NO ) + changedOption.displayAlignment = Qt::AlignRight; + KompareListViewItem* item = static_cast( static_cast( parent() )->itemFromIndex( index ) ); + item->paintCell( painter, changedOption, column ); +} + +QSize KompareListViewItemDelegate::sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const +{ + KompareListViewItem* item = static_cast( static_cast( parent() )->itemFromIndex( index ) ); + QSize hint = QStyledItemDelegate::sizeHint( option, index ); + return QSize( hint.width() + ITEM_MARGIN, item->height() ); +} + +KompareListViewItem::KompareListViewItem( KompareListView* parent, int type ) + : QTreeWidgetItem( parent, type ), + m_scrollId( 0 ), + m_height( 0 ), + m_paintHeight( 0 ), + m_paintOffset( parent->nextPaintOffset() ) +{ +// kDebug(8104) << "Created KompareListViewItem with scroll id " << m_scrollId << endl; +} + +KompareListViewItem::KompareListViewItem( KompareListView* parent, KompareListViewItem* after, int type ) + : QTreeWidgetItem( parent, after, type ), + m_scrollId( after->scrollId() + after->maxHeight() ), + m_height( 0 ), + m_paintHeight( 0 ), + m_paintOffset( parent->nextPaintOffset() ) +{ +// kDebug(8104) << "Created KompareListViewItem with scroll id " << m_scrollId << endl; +} + +KompareListViewItem::KompareListViewItem( KompareListViewItem* parent, int type ) + : QTreeWidgetItem( parent, type ), + m_scrollId( 0 ), + m_height( 0 ), + m_paintHeight( 0 ), + m_paintOffset( parent->kompareListView()->nextPaintOffset() ) +{ +} + +KompareListViewItem::KompareListViewItem( KompareListViewItem* parent, KompareListViewItem* /*after*/, int type ) + : QTreeWidgetItem( parent, type ), + m_scrollId( 0 ), + m_height( 0 ), + m_paintHeight( 0 ), + m_paintOffset( parent->kompareListView()->nextPaintOffset() ) +{ +} + +int KompareListViewItem::height() const +{ + return m_height; +} + +void KompareListViewItem::setHeight( int h ) +{ + m_height = m_paintHeight = h; + // QTreeWidget doesn't like zero height, fudge around it. + m_height -= m_paintOffset; + if( m_height <= 0 ) { + kompareListView()->setNextPaintOffset( 1 - m_height ); + m_height = 1; + } else kompareListView()->setNextPaintOffset( 0 ); +} + +int KompareListViewItem::paintHeight() const +{ + return m_paintHeight; +} + +int KompareListViewItem::paintOffset() const +{ + return m_paintOffset; +} + +bool KompareListViewItem::isCurrent() const +{ + return treeWidget()->currentItem() == this; +} + +KompareListView* KompareListViewItem::kompareListView() const +{ + return (KompareListView*)treeWidget(); +} + +void KompareListViewItem::paintCell( QPainter* p, const QStyleOptionViewItem& option, int column ) +{ + // Default implementation for zero-height items. + // We have to paint the item which shines through or we'll end up with glitches. + KompareListViewItem* nextItem = (KompareListViewItem*)kompareListView()->itemBelow(this); + if( nextItem ) { + QStyleOptionViewItemV4 changedOption = option; + changedOption.rect.translate( 0, height() ); + nextItem->paintCell( p, changedOption, column ); + } +} + +KompareListViewDiffItem::KompareListViewDiffItem( KompareListView* parent, Difference* difference ) + : KompareListViewItem( parent, Diff ), + m_difference( difference ), + m_sourceItem( 0L ), + m_destItem( 0L ) +{ + init(); +} + +KompareListViewDiffItem::KompareListViewDiffItem( KompareListView* parent, KompareListViewItem* after, Difference* difference ) + : KompareListViewItem( parent, after, Diff ), + m_difference( difference ), + m_sourceItem( 0L ), + m_destItem( 0L ) +{ + init(); +} + +KompareListViewDiffItem::~KompareListViewDiffItem() +{ + m_difference = 0; +} + +void KompareListViewDiffItem::init() +{ + setHeight( 0 ); + setExpanded( true ); + int nextPaintOffset = kompareListView()->nextPaintOffset(); + m_destItem = new KompareListViewLineContainerItem( this, false ); + kompareListView()->setNextPaintOffset(nextPaintOffset); + m_sourceItem = new KompareListViewLineContainerItem( this, true ); + setVisibility(); +} + +void KompareListViewDiffItem::setVisibility() +{ + m_sourceItem->setHidden( !(kompareListView()->isSource() || m_difference->applied()) ); + m_destItem->setHidden( !m_sourceItem->isHidden() ); +} + +void KompareListViewDiffItem::applyDifference( bool apply ) +{ + kDebug(8104) << "KompareListViewDiffItem::applyDifference( " << apply << " )" << endl; + setVisibility(); +} + +int KompareListViewDiffItem::maxHeight() +{ + int lines = qMax( m_difference->sourceLineCount(), m_difference->destinationLineCount() ); + if( lines == 0 ) + return BLANK_LINE_HEIGHT; + else + return lines * treeWidget()->fontMetrics().height(); +} + +KompareListViewLineContainerItem::KompareListViewLineContainerItem( KompareListViewDiffItem* parent, bool isSource ) + : KompareListViewItem( parent, Container ), + m_blankLineItem( 0 ), + m_isSource( isSource ) +{ +// kDebug(8104) << "isSource ? " << (isSource ? " Yes!" : " No!") << endl; + setHeight( 0 ); + setExpanded( true ); + + int lines = lineCount(); + int line = lineNumber(); +// kDebug(8104) << "LineNumber : " << lineNumber() << endl; + if( lines == 0 ) { + m_blankLineItem = new KompareListViewBlankLineItem( this ); + return; + } + + for( int i = 0; i < lines; i++, line++ ) { + new KompareListViewLineItem( this, line, lineAt( i ) ); + } +} + +KompareListViewLineContainerItem::~KompareListViewLineContainerItem() +{ +} + +KompareListViewDiffItem* KompareListViewLineContainerItem::diffItemParent() const +{ + return (KompareListViewDiffItem*)parent(); +} + +int KompareListViewLineContainerItem::lineCount() const +{ + return m_isSource ? diffItemParent()->difference()->sourceLineCount() : + diffItemParent()->difference()->destinationLineCount(); +} + +int KompareListViewLineContainerItem::lineNumber() const +{ + return m_isSource ? diffItemParent()->difference()->sourceLineNumber() : + diffItemParent()->difference()->destinationLineNumber(); +} + +DifferenceString* KompareListViewLineContainerItem::lineAt( int i ) const +{ + return m_isSource ? diffItemParent()->difference()->sourceLineAt( i ) : + diffItemParent()->difference()->destinationLineAt( i ); +} + +KompareListViewLineItem::KompareListViewLineItem( KompareListViewLineContainerItem* parent, int line, DifferenceString* text ) + : KompareListViewItem( parent, Line ) +{ + init( line, text ); +} + +KompareListViewLineItem::KompareListViewLineItem( KompareListViewLineContainerItem* parent, int line, DifferenceString* text, int type ) + : KompareListViewItem( parent, type ) +{ + init( line, text ); +} + +KompareListViewLineItem::~KompareListViewLineItem() +{ + m_text = 0; +} + +void KompareListViewLineItem::init( int line, DifferenceString* text ) +{ + setHeight( treeWidget()->fontMetrics().height() ); + setText( COL_LINE_NO, QString::number( line ) ); + setText( COL_MAIN, text->string() ); + m_text = text; +} + +void KompareListViewLineItem::paintCell( QPainter* p, const QStyleOptionViewItem& option, int column ) +{ + int width = option.rect.width(); + Qt::Alignment align = option.displayAlignment; + + p->setRenderHint(QPainter::Antialiasing); + p->translate(option.rect.topLeft()); + p->translate(0, -paintOffset()); + + QColor bg( Qt::white ); // Always make the background white when it is not a real difference + if ( diffItemParent()->difference()->type() == Difference::Unchanged ) + { + if ( column == COL_LINE_NO ) + { + bg = QColor( Qt::lightGray ); + } + } + else + { + bg = kompareListView()->settings()->colorForDifferenceType( + diffItemParent()->difference()->type(), + diffItemParent()->isCurrent(), + diffItemParent()->difference()->applied() ); + } + + // Paint background + p->fillRect( 0, 0, width, paintHeight(), bg ); + + // Paint foreground + if ( diffItemParent()->difference()->type() == Difference::Unchanged ) + p->setPen( QColor( Qt::darkGray ) ); // always make normal text gray + else + p->setPen( QColor( Qt::black ) ); // make text with changes black + + paintText( p, bg, column, width, align ); + + // Paint darker lines around selected item + if ( diffItemParent()->isCurrent() ) + { + p->translate(0.5,0.5); + p->setPen( bg.dark(135) ); + QTreeWidgetItem* parentItem = parent(); + if ( this == parentItem->child( 0 ) ) + p->drawLine( 0, 0, width, 0 ); + if ( this == parentItem->child( parentItem->childCount() - 1 ) ) + p->drawLine( 0, paintHeight() - 1, width, paintHeight() - 1 ); + } + + p->resetTransform(); +} + +void KompareListViewLineItem::paintText( QPainter* p, const QColor& bg, int column, int width, int align ) +{ + if ( column == COL_MAIN ) + { + QString textChunk; + int offset = ITEM_MARGIN; + int prevValue = 0; + int charsDrawn = 0; + int chunkWidth; + QBrush changeBrush( bg, Qt::Dense3Pattern ); + QBrush normalBrush( bg, Qt::SolidPattern ); + QBrush brush; + + if ( m_text->string().isEmpty() ) + { + p->fillRect( 0, 0, width, paintHeight(), normalBrush ); + return; + } + + p->fillRect( 0, 0, offset, paintHeight(), normalBrush ); + + if ( !m_text->markerList().isEmpty() ) + { + MarkerListConstIterator markerIt = m_text->markerList().begin(); + MarkerListConstIterator mEnd = m_text->markerList().end(); + Marker* m = *markerIt; + + for ( ; markerIt != mEnd; ++markerIt ) + { + m = *markerIt; + textChunk = m_text->string().mid( prevValue, m->offset() - prevValue ); +// kDebug(8104) << "TextChunk = \"" << textChunk << "\"" << endl; +// kDebug(8104) << "c->offset() = " << c->offset() << endl; +// kDebug(8104) << "prevValue = " << prevValue << endl; + expandTabs(textChunk, kompareListView()->settings()->m_tabToNumberOfSpaces, charsDrawn); + charsDrawn += textChunk.length(); + prevValue = m->offset(); + if ( m->type() == Marker::End ) + { + QFont font( p->font() ); + font.setBold( true ); + p->setFont( font ); +// p->setPen( Qt::blue ); + brush = changeBrush; + } + else + { + QFont font( p->font() ); + font.setBold( false ); + p->setFont( font ); +// p->setPen( Qt::black ); + brush = normalBrush; + } + chunkWidth = p->fontMetrics().width( textChunk ); + p->fillRect( offset, 0, chunkWidth, paintHeight(), brush ); + p->drawText( offset, 0, + chunkWidth, paintHeight(), + align, textChunk ); + offset += chunkWidth; + } + } + if ( prevValue < m_text->string().length() ) + { + // Still have to draw some string without changes + textChunk = m_text->string().mid( prevValue, qMax( 1, m_text->string().length() - prevValue ) ); + expandTabs(textChunk, kompareListView()->settings()->m_tabToNumberOfSpaces, charsDrawn); +// kDebug(8104) << "TextChunk = \"" << textChunk << "\"" << endl; + QFont font( p->font() ); + font.setBold( false ); + p->setFont( font ); + chunkWidth = p->fontMetrics().width( textChunk ); + p->fillRect( offset, 0, chunkWidth, paintHeight(), normalBrush ); + p->drawText( offset, 0, + chunkWidth, paintHeight(), + align, textChunk ); + offset += chunkWidth; + } + p->fillRect( offset, 0, width - offset, paintHeight(), normalBrush ); + } + else + { + p->fillRect( 0, 0, width, paintHeight(), bg ); + p->drawText( ITEM_MARGIN, 0, + width - ITEM_MARGIN, paintHeight(), + align, text( column ) ); + } +} + +void KompareListViewLineItem::expandTabs(QString& text, int tabstop, int startPos) const +{ + int index; + while((index = text.indexOf(QChar(9)))!= -1) + text.replace(index, 1, QString(tabstop-((startPos+index)%tabstop),' ')); +} + +KompareListViewDiffItem* KompareListViewLineItem::diffItemParent() const +{ + KompareListViewLineContainerItem* p = (KompareListViewLineContainerItem*)parent(); + return p->diffItemParent(); +} + +KompareListViewBlankLineItem::KompareListViewBlankLineItem( KompareListViewLineContainerItem* parent ) + : KompareListViewLineItem( parent, 0, new DifferenceString(), Blank ) +{ + setHeight( BLANK_LINE_HEIGHT ); +} + +void KompareListViewBlankLineItem::paintText( QPainter* p, const QColor& bg, int column, int width, int /* align */ ) +{ + if ( column == COL_MAIN ) + { + QBrush normalBrush( bg, Qt::SolidPattern ); + p->fillRect( 0, 0, width, paintHeight(), normalBrush ); + } +} + +KompareListViewHunkItem::KompareListViewHunkItem( KompareListView* parent, DiffHunk* hunk, bool zeroHeight ) + : KompareListViewItem( parent, Hunk ), + m_zeroHeight( zeroHeight ), + m_hunk( hunk ) +{ + setHeight( maxHeight() ); + setFlags( flags() & ~Qt::ItemIsSelectable ); +} + +KompareListViewHunkItem::KompareListViewHunkItem( KompareListView* parent, KompareListViewItem* after, DiffHunk* hunk, bool zeroHeight ) + : KompareListViewItem( parent, after, Hunk ), + m_zeroHeight( zeroHeight ), + m_hunk( hunk ) +{ + setHeight( maxHeight() ); + setFlags( flags() & ~Qt::ItemIsSelectable ); +} + +KompareListViewHunkItem::~KompareListViewHunkItem() +{ + m_hunk = 0; +} + +int KompareListViewHunkItem::maxHeight() +{ + if( m_zeroHeight ) { + return 0; + } else if( m_hunk->function().isEmpty() ) { + return HUNK_LINE_HEIGHT; + } else { + return treeWidget()->fontMetrics().height(); + } +} + +void KompareListViewHunkItem::paintCell( QPainter* p, const QStyleOptionViewItem& option, int column ) +{ + if( m_zeroHeight ) { + KompareListViewItem::paintCell( p, option, column ); + } else { + int x = option.rect.left(); + int y = option.rect.top() - paintOffset(); + int width = option.rect.width(); + Qt::Alignment align = option.displayAlignment; + + p->fillRect( x, y, width, paintHeight(), QColor( Qt::lightGray ) ); // Hunk headers should be lightgray + p->setPen( QColor( Qt::black ) ); // Text color in hunk should be black + if( column == COL_MAIN ) { + p->drawText( x + ITEM_MARGIN, y, width - ITEM_MARGIN, paintHeight(), + align, m_hunk->function() ); + } + } +} + +#include "komparelistview.moc" diff --git a/kompare/komparepart/komparelistview.h b/kompare/komparepart/komparelistview.h new file mode 100644 index 00000000..7c5c26c3 --- /dev/null +++ b/kompare/komparepart/komparelistview.h @@ -0,0 +1,271 @@ +/*************************************************************************** + komparelistview.h + ----------------- + begin : Sun Mar 4 2001 + Copyright 2001-2004,2009 Otto Bruggeman + Copyright 2001-2003 John Firebaugh + Copyright 2004 Jeff Snyder + Copyright 2011 Kevin Kofler +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 KOMPARELISTVIEW_H +#define KOMPARELISTVIEW_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Diff2 { +class DiffModel; +class DiffHunk; +class Difference; +class DifferenceString; +} +class ViewSettings; +class KompareSplitter; +class KompareListView; +class KompareListViewItem; +class KompareListViewItemDelegate; +class KompareListViewDiffItem; +class KompareListViewLineContainerItem; + +class KompareListView : public QTreeWidget +{ + Q_OBJECT + + friend class KompareListViewItemDelegate; + +public: + KompareListView( bool isSource, ViewSettings* settings, QWidget* parent, const char* name = 0 ); + virtual ~KompareListView(); + + KompareListViewItem* itemAtIndex( int i ); + int firstVisibleDifference(); + int lastVisibleDifference(); + QRect itemRect( int i ); + int minScrollId(); + int maxScrollId(); + int contentsHeight(); + int contentsWidth(); + int visibleHeight(); + int visibleWidth(); + int contentsX(); + int contentsY(); + int nextPaintOffset() const; + void setNextPaintOffset(int offset); + + bool isSource() const { return m_isSource; }; + ViewSettings* settings() const { return m_settings; }; + + void setSelectedDifference( const Diff2::Difference* diff, bool scroll ); + +public slots: + void slotSetSelection( const Diff2::DiffModel* model, const Diff2::Difference* diff ); + void slotSetSelection( const Diff2::Difference* diff ); + void setXOffset( int x ); + void scrollToId( int id ); + int scrollId(); + void slotApplyDifference( bool apply ); + void slotApplyAllDifferences( bool apply ); + void slotApplyDifference( const Diff2::Difference* diff, bool apply ); + +signals: + void differenceClicked( const Diff2::Difference* diff ); + void applyDifference( bool apply ); + void resized(); + +protected: + virtual void wheelEvent( QWheelEvent* e ); + virtual void resizeEvent( QResizeEvent* e ); + virtual void mousePressEvent ( QMouseEvent * e ); + virtual void mouseDoubleClickEvent ( QMouseEvent* ); + virtual void mouseReleaseEvent ( QMouseEvent * ) {}; + virtual void mouseMoveEvent ( QMouseEvent * ) {}; + +private: + QRect totalVisualItemRect( QTreeWidgetItem* item ); + KompareListViewDiffItem* diffItemAt( const QPoint& pos ); + void renumberLines( void ); + + QList m_items; + QHash m_itemDict; + bool m_isSource; + ViewSettings* m_settings; + int m_scrollId; + int m_maxMainWidth; + const Diff2::DiffModel* m_selectedModel; + const Diff2::Difference* m_selectedDifference; + int m_nextPaintOffset; +}; + +class KompareListViewFrame : public QFrame +{ + Q_OBJECT + +public: + KompareListViewFrame( bool isSource, ViewSettings* settings, KompareSplitter* parent, const char* name = 0 ); + virtual ~KompareListViewFrame() {}; + KompareListView* view() { return &m_view; }; + +public slots: + void slotSetModel( const Diff2::DiffModel* model ); + +private: + KompareListView m_view; + QLabel m_label; + QVBoxLayout m_layout; +}; + +class KompareListViewItemDelegate : public QStyledItemDelegate +{ + Q_OBJECT + +public: + KompareListViewItemDelegate( QObject* parent ); + virtual ~KompareListViewItemDelegate(); + virtual void paint( QPainter* painter, const QStyleOptionViewItem& option, const QModelIndex& index ) const; + virtual QSize sizeHint( const QStyleOptionViewItem& option, const QModelIndex& index ) const; +}; + +class KompareListViewItem : public QTreeWidgetItem +{ +public: + KompareListViewItem( KompareListView* parent, int type ); + KompareListViewItem( KompareListView* parent, KompareListViewItem* after, int type ); + KompareListViewItem( KompareListViewItem* parent, int type ); + KompareListViewItem( KompareListViewItem* parent, KompareListViewItem* after, int type ); + + virtual void paintCell( QPainter* p, const QStyleOptionViewItem& option, int column ); + + void repaint(); + int height() const; + void setHeight( int h ); + int paintHeight() const; + int paintOffset() const; + bool isCurrent() const; + int scrollId() const { return m_scrollId; }; + + virtual int maxHeight() = 0; + + KompareListView* kompareListView() const; + + enum ListViewItemType { Diff = QTreeWidgetItem::UserType + 1, Container = QTreeWidgetItem::UserType + 2, Line = QTreeWidgetItem::UserType + 3, Blank = QTreeWidgetItem::UserType + 4, Hunk = QTreeWidgetItem::UserType + 5 }; + +private: + int m_scrollId; + int m_height; + int m_paintHeight; + int m_paintOffset; +}; + +class KompareListViewDiffItem : public KompareListViewItem +{ +public: + KompareListViewDiffItem( KompareListView* parent, Diff2::Difference* difference ); + KompareListViewDiffItem( KompareListView* parent, KompareListViewItem* after, Diff2::Difference* difference ); + ~KompareListViewDiffItem(); + + void applyDifference( bool apply ); + + Diff2::Difference* difference() { return m_difference; }; + + virtual int maxHeight(); + +private: + void init(); + void setVisibility(); + +private: + Diff2::Difference* m_difference; + KompareListViewLineContainerItem* m_sourceItem; + KompareListViewLineContainerItem* m_destItem; +}; + +class KompareListViewLineItem; +class KompareListViewBlankLineItem; + +class KompareListViewLineContainerItem : public KompareListViewItem +{ +public: + KompareListViewLineContainerItem( KompareListViewDiffItem* parent, bool isSource ); + ~KompareListViewLineContainerItem(); + + virtual int maxHeight() { return 0; } + + KompareListViewDiffItem* diffItemParent() const; + +private: + int lineCount() const; + int lineNumber() const; + Diff2::DifferenceString* lineAt( int i ) const; + +private: + KompareListViewBlankLineItem* m_blankLineItem; + bool m_isSource; +}; + +class KompareListViewLineItem : public KompareListViewItem +{ +public: + KompareListViewLineItem( KompareListViewLineContainerItem* parent, int line, Diff2::DifferenceString* text ); + KompareListViewLineItem( KompareListViewLineContainerItem* parent, int line, Diff2::DifferenceString* text, int type ); + ~KompareListViewLineItem(); + + virtual int maxHeight() { return 0; } + + virtual void paintCell( QPainter* p, const QStyleOptionViewItem& option, int column ); + + KompareListViewDiffItem* diffItemParent() const; + +protected: + virtual void paintText( QPainter* p, const QColor& bg, int column, int width, int align ); + +private: + void init( int line, Diff2::DifferenceString* text ); + void expandTabs(QString& text, int tabstop, int startPos = 0) const; + +private: + Diff2::DifferenceString* m_text; +}; + +class KompareListViewBlankLineItem : public KompareListViewLineItem +{ +public: + KompareListViewBlankLineItem( KompareListViewLineContainerItem* parent ); + +protected: + virtual void paintText( QPainter* p, const QColor& bg, int column, int width, int align ); +}; + +class KompareListViewHunkItem : public KompareListViewItem +{ +public: + KompareListViewHunkItem( KompareListView* parent, Diff2::DiffHunk* hunk, bool zeroHeight = false ); + KompareListViewHunkItem( KompareListView* parent, KompareListViewItem* after, Diff2::DiffHunk* hunk, bool zeroHeight= false ); + ~KompareListViewHunkItem(); + + virtual void paintCell( QPainter* p, const QStyleOptionViewItem& option, int column ); + + virtual int maxHeight(); + +private: + bool m_zeroHeight; + Diff2::DiffHunk* m_hunk; +}; + +#endif diff --git a/kompare/komparepart/komparepart.desktop b/kompare/komparepart/komparepart.desktop new file mode 100644 index 00000000..064bb2a6 --- /dev/null +++ b/kompare/komparepart/komparepart.desktop @@ -0,0 +1,72 @@ +[Desktop Entry] +GenericComment=A part to graphically show the difference between 2 files +Name=KomparePart +Name[af]=K-vergelyk-deel +Name[ast]=KomparePart +Name[bg]=KomparePart +Name[br]=KomparePart +Name[bs]=KomparePart +Name[ca]=KomparePart +Name[ca@valencia]=KomparePart +Name[cs]=KomparePart +Name[cy]=KomparePart +Name[da]=KomparePart +Name[de]=Einbettungsfähige Komponente von Kompare +Name[el]=KomparePart +Name[en_GB]=KomparePart +Name[eo]=Komparo-parto +Name[es]=KomparePart +Name[et]=KomparePart +Name[eu]=KomparePart +Name[fi]=KomparePart +Name[fr]=Composant de Kompare +Name[ga]=KomparePart +Name[gl]=KomparePart +Name[he]=רכיב Kompare +Name[hr]=KomparePart +Name[hu]=KomparePart +Name[is]=KomparePart +Name[it]=KomparePart +Name[ja]=KomparePart +Name[kk]=KomparePart +Name[km]=KomparePart +Name[ko]=KomparePart +Name[lt]=KomparePart +Name[lv]=KomparePart +Name[mr]=कंपेअर-पार्ट +Name[ms]=KomparePart +Name[nb]=KomparePart +Name[nds]=KomparePart +Name[ne]=KomparePart +Name[nl]=KomparePart +Name[nn]=KomparePart +Name[pa]=KomparePart +Name[pl]=Moduł Kompare +Name[pt]=KomparePart +Name[pt_BR]=KomparePart +Name[ro]=Componentă Kompare +Name[ru]=Компонент утилиты сравнения файлов +Name[sk]=KomparePart +Name[sl]=KomparePart +Name[sq]=KomparePart +Name[sr]=К‑поређење део +Name[sr@ijekavian]=К‑поређење део +Name[sr@ijekavianlatin]=K‑poređenje deo +Name[sr@latin]=K‑poređenje deo +Name[sv]=Kompare-del +Name[ta]=கோம்பெர் உறுப்பு +Name[tg]=Қисмати утилитҳои баробаркунии файлҳо +Name[tr]=KomparePart +Name[ug]=KomparePart +Name[uk]=KomparePart +Name[vi]=KomparePart +Name[xh]=KomparePart +Name[x-test]=xxKomparePartxx +Name[zh_CN]=KomparePart +Name[zh_TW]=KomparePart +MimeType=text/x-patch; +ServiceTypes=Kompare/ViewPart,KParts/ReadWritePart,KParts/ReadOnlyPart +X-KDE-Library=komparepart +Type=Service +Icon=kompare +InitialPreference=10 diff --git a/kompare/komparepart/komparepartui.rc b/kompare/komparepart/komparepartui.rc new file mode 100644 index 00000000..2329f781 --- /dev/null +++ b/kompare/komparepart/komparepartui.rc @@ -0,0 +1,48 @@ + + + + &File + + + + + + + + + + + + + &Difference + + + + + + + + + + + + &Settings + + + + + + + + + + + + + + + + + + + diff --git a/kompare/komparepart/kompareprefdlg.cpp b/kompare/komparepart/kompareprefdlg.cpp new file mode 100644 index 00000000..0b18696a --- /dev/null +++ b/kompare/komparepart/kompareprefdlg.cpp @@ -0,0 +1,160 @@ +/*************************************************************************** + kompareprefdlg.cpp + ------------------ + begin : Sun Mar 4 2001 + Copyright 2001-2009 Otto Bruggeman + Copyright 2001-2003 John Firebaugh + Copyright 2007 Kevin Kofler +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 "kompareprefdlg.h" + +#include +#include +#include +#include +#include + +#include "diffpage.h" +#include "viewpage.h" + +// implementation + +KomparePrefDlg::KomparePrefDlg( ViewSettings* viewSets, DiffSettings* diffSets ) : KPageDialog( 0 ) +{ + setFaceType( KPageDialog::List ); + setWindowTitle( i18n( "Preferences" ) ); + setButtons( Help|Default|Ok|Apply|Cancel ); + setDefaultButton( Ok ); + setModal( true ); + showButtonSeparator( true ); + + // ok i need some stuff in that pref dlg... + //setIconListAllVisible(true); + + m_viewPage = new ViewPage(); + KPageWidgetItem *item = addPage( m_viewPage, i18n( "View" ) ); + item->setIcon( KIcon( "preferences-desktop-theme" ) ); + item->setHeader( i18n( "View Settings" ) ); + m_viewPage->setSettings( viewSets ); + + m_diffPage = new DiffPage(); + item = addPage( m_diffPage, i18n( "Diff" ) ); + item->setIcon( KIcon( "text-x-patch" ) ); + item->setHeader( i18n( "Diff Settings" ) ); + m_diffPage->setSettings( diffSets ); + +// frame = addVBoxPage( i18n( "" ), i18n( "" ), UserIcon( "" ) ); + + connect( this, SIGNAL(defaultClicked()), SLOT(slotDefault()) ); + connect( this, SIGNAL(helpClicked()), SLOT(slotHelp()) ); + connect( this, SIGNAL(applyClicked()), SLOT(slotApply()) ); + connect( this, SIGNAL(okClicked()), SLOT(slotOk()) ); + connect( this, SIGNAL(cancelClicked()), SLOT(slotCancel()) ); + + adjustSize(); +} + +KomparePrefDlg::~KomparePrefDlg() +{ + +} + +/** No descriptions */ +void KomparePrefDlg::slotDefault() +{ + kDebug(8103) << "SlotDefault called -> Settings should be restored to defaults..." << endl; + // restore all defaults in the options... + m_viewPage->setDefaults(); + m_diffPage->setDefaults(); +} + +/** No descriptions */ +void KomparePrefDlg::slotHelp() +{ + // figure out the current active page + QWidget* currentpage = currentPage()->widget(); + if ( dynamic_cast(currentpage) ) + { + // figure out the active tab + int currentTab = static_cast(currentpage)->m_tabWidget->currentIndex(); + switch ( currentTab ) + { + case 0: + KToolInvocation::invokeHelp( "appearance" ); + break; + case 1: + KToolInvocation::invokeHelp( "fonts" ); + break; + default: + KToolInvocation::invokeHelp( "view-settings" ); + } + } + else if ( dynamic_cast(currentpage) ) + { + // figure out the active tab + int currentTab = static_cast(currentpage)->m_tabWidget->currentIndex(); + switch ( currentTab ) + { + case 0: + KToolInvocation::invokeHelp( "diff" ); + break; + case 1: + KToolInvocation::invokeHelp( "diff-format" ); + break; + case 2: + KToolInvocation::invokeHelp( "options" ); + break; + case 3: + KToolInvocation::invokeHelp( "exclude" ); + break; + default: + KToolInvocation::invokeHelp( "diff-settings" ); + } + } + else // Fallback since we had not added the code for the page/tab or forgotten about it + KToolInvocation::invokeHelp( "configure-preferences" ); +} + +/** No descriptions */ +void KomparePrefDlg::slotApply() +{ + kDebug(8103) << "SlotApply called -> Settings should be applied..." << endl; + // well apply the settings that are currently selected + m_viewPage->apply(); + m_diffPage->apply(); + + emit configChanged(); +} + +/** No descriptions */ +void KomparePrefDlg::slotOk() +{ + kDebug(8103) << "SlotOk called -> Settings should be applied..." << endl; + // Apply the settings that are currently selected + m_viewPage->apply(); + m_diffPage->apply(); + + //accept(); +} + +/** No descriptions */ +void KomparePrefDlg::slotCancel() +{ + // discard the current settings and use the present ones + m_viewPage->restore(); + m_diffPage->restore(); + + //reject(); +} + +#include "kompareprefdlg.moc" diff --git a/kompare/komparepart/kompareprefdlg.h b/kompare/komparepart/kompareprefdlg.h new file mode 100644 index 00000000..ee6699cf --- /dev/null +++ b/kompare/komparepart/kompareprefdlg.h @@ -0,0 +1,56 @@ +/*************************************************************************** + kompareprefdlg.h + ---------------- + begin : Sun Mar 4 2001 + Copyright 2001-2009 Otto Bruggeman + Copyright 2001-2003 John Firebaugh + Copyright 2007 Kevin Kofler +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 KOMPAREPREFDLG_H +#define KOMPAREPREFDLG_H + +#include + +class DiffPage; +class DiffSettings; +class ViewPage; +class ViewSettings; + +class KomparePrefDlg : public KPageDialog +{ +Q_OBJECT +public: + KomparePrefDlg( ViewSettings*, DiffSettings* ); + ~KomparePrefDlg(); + +protected slots: + /** No descriptions */ + virtual void slotOk(); + /** No descriptions */ + virtual void slotApply(); + /** No descriptions */ + virtual void slotHelp(); + /** No descriptions */ + virtual void slotDefault(); + /** No descriptions */ + virtual void slotCancel(); + +signals: + void configChanged(); + +private: + ViewPage* m_viewPage; + DiffPage* m_diffPage; +}; + +#endif diff --git a/kompare/komparepart/komparesaveoptionsbase.cpp b/kompare/komparepart/komparesaveoptionsbase.cpp new file mode 100644 index 00000000..e16ef1a2 --- /dev/null +++ b/kompare/komparepart/komparesaveoptionsbase.cpp @@ -0,0 +1,48 @@ +/*************************************************************************** + komparesaveoptionsbase.cpp + -------------------------- + Converted from komparesaveoptionsbase.ui using uic3, Tue Dec 4 2007 + Copyright 2001-2003 Otto Bruggeman + Copyright 2001-2003 John Firebaugh + Copyright 2007-2011 Kevin Kofler +****************************************************************************/ + + +/*************************************************************************** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the 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 "komparesaveoptionsbase.h" + +#include +/* + * Constructs a KompareSaveOptionsBase as a child of 'parent', with the + * name 'name' and widget flags set to 'f'. + */ +KompareSaveOptionsBase::KompareSaveOptionsBase(QWidget* parent, Qt::WindowFlags fl) + : QWidget(parent, fl) +{ + setupUi(this); + +} + +/* + * Destroys the object and frees any allocated resources + */ +KompareSaveOptionsBase::~KompareSaveOptionsBase() +{ + // no need to delete child widgets, Qt does it all for us +} + +/* + * Sets the strings of the subwidgets using the current + * language. + */ +void KompareSaveOptionsBase::languageChange() +{ + retranslateUi(this); +} + diff --git a/kompare/komparepart/komparesaveoptionsbase.h b/kompare/komparepart/komparesaveoptionsbase.h new file mode 100644 index 00000000..b21fa06d --- /dev/null +++ b/kompare/komparepart/komparesaveoptionsbase.h @@ -0,0 +1,37 @@ +/*************************************************************************** + komparesaveoptionsbase.h + ------------------------ + Converted from komparesaveoptionsbase.ui using uic3, Tue Dec 4 2007 + Copyright 2001-2003 Otto Bruggeman + Copyright 2001-2003 John Firebaugh + Copyright 2007-2011 Kevin Kofler +****************************************************************************/ + +/*************************************************************************** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the 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 KOMPARESAVEOPTIONSBASE_H +#define KOMPARESAVEOPTIONSBASE_H + +#include + +#include "ui_komparesaveoptionsbase.h" + +class KompareSaveOptionsBase : public QWidget, public Ui::KompareSaveOptionsBase +{ + Q_OBJECT + +public: + explicit KompareSaveOptionsBase(QWidget* parent = 0, Qt::WindowFlags fl = 0); + ~KompareSaveOptionsBase(); + +protected slots: + virtual void languageChange(); + +}; + +#endif // KOMPARESAVEOPTIONSBASE_H diff --git a/kompare/komparepart/komparesaveoptionsbase.ui b/kompare/komparepart/komparesaveoptionsbase.ui new file mode 100644 index 00000000..92fccab5 --- /dev/null +++ b/kompare/komparepart/komparesaveoptionsbase.ui @@ -0,0 +1,284 @@ + + KompareSaveOptionsBase + + + + 0 + 0 + 558 + 399 + + + + + 0 + + + 6 + + + + + + 1 + 1 + 0 + 0 + + + + Run Diff In + + + + 11 + + + 6 + + + + + + 7 + 5 + 0 + 0 + + + + + + + + + + + + 3 + 3 + 0 + 0 + + + + Command Line + + + Qt::AlignVCenter|Qt::AlignLeft + + + + 11 + + + 6 + + + + + cd dir && diff -udHprNa -- source destination + + + false + + + + + + + + + + Options + + + + 11 + + + 6 + + + + + Look for smaller changes + + + true + + + + + + + Optimize for large files + + + true + + + + + + + Ignore changes in case + + + + + + + Expand tabs to spaces + + + + + + + Ignore added or removed empty lines + + + + + + + Ignore changes in whitespace + + + + + + + Show function names + + + true + + + + + + + Compare folders recursively + + + true + + + + + + + Treat new files as empty + + + true + + + false + + + + + + + + + + Format + + + + 11 + + + 6 + + + + + Context + + + + + + + Ed + + + + + + + Normal + + + + + + + RCS + + + + + + + Unified + + + true + + + + + + + Side-by-side + + + false + + + + + + + 0 + + + 6 + + + + + Number of context lines: + + + false + + + + + + + true + + + 65535 + + + 3 + + + + + + + + + + + diff --git a/kompare/komparepart/komparesaveoptionswidget.cpp b/kompare/komparepart/komparesaveoptionswidget.cpp new file mode 100644 index 00000000..06530d85 --- /dev/null +++ b/kompare/komparepart/komparesaveoptionswidget.cpp @@ -0,0 +1,225 @@ +/*************************************************************************** + komparesaveoptionswidget.cpp + ---------------------------- + begin : Sun Mar 4 2001 + Copyright 2001-2003 Otto Bruggeman + Copyright 2001-2003 John Firebaugh + Copyright 2007-2011 Kevin Kofler +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 "komparesaveoptionswidget.h" + +#include +#include +#include +#include + +#include +#include +#include + +#include "diffsettings.h" + +KompareSaveOptionsWidget::KompareSaveOptionsWidget( QString source, QString destination, + DiffSettings * settings, QWidget * parent ) + : KompareSaveOptionsBase( parent ) + , m_source( source ) + , m_destination( destination ) + , m_FormatBG( new QButtonGroup(this) ) +{ + setObjectName("save options"); + + m_settings = settings; + + m_directoryRequester->setMode( + KFile::ExistingOnly | KFile::Directory | KFile::LocalOnly ); + + KUrl sourceURL; + KUrl destinationURL; + sourceURL.setPath( source ); + destinationURL.setPath( destination ); + + // Find a common root. + KUrl root( sourceURL ); + while( root.isValid() && !root.isParentOf( destinationURL ) && root != root.upUrl() ) { + root = root.upUrl(); + } + + // If we found a common root, change to that directory and + // strip the common part from source and destination. + if( root.isValid() && root != root.upUrl() ) { + m_directoryRequester->setUrl( root.url() ); + } + + connect( m_SmallerChangesCB, SIGNAL(toggled(bool)), SLOT(updateCommandLine()) ); + connect( m_LargeFilesCB, SIGNAL(toggled(bool)), SLOT(updateCommandLine()) ); + connect( m_IgnoreCaseCB, SIGNAL(toggled(bool)), SLOT(updateCommandLine()) ); + connect( m_ExpandTabsCB, SIGNAL(toggled(bool)), SLOT(updateCommandLine()) ); + connect( m_IgnoreEmptyLinesCB, SIGNAL(toggled(bool)), SLOT(updateCommandLine()) ); + connect( m_IgnoreWhiteSpaceCB, SIGNAL(toggled(bool)), SLOT(updateCommandLine()) ); + connect( m_FunctionNamesCB, SIGNAL(toggled(bool)), SLOT(updateCommandLine()) ); + connect( m_RecursiveCB, SIGNAL(toggled(bool)), SLOT(updateCommandLine()) ); + connect( m_NewFilesCB, SIGNAL(toggled(bool)), SLOT(updateCommandLine()) ); + connect( m_ContextRB, SIGNAL(toggled(bool)), SLOT(updateCommandLine()) ); + connect( m_EdRB, SIGNAL(toggled(bool)), SLOT(updateCommandLine()) ); + connect( m_NormalRB, SIGNAL(toggled(bool)), SLOT(updateCommandLine()) ); + connect( m_RCSRB, SIGNAL(toggled(bool)), SLOT(updateCommandLine()) ); + connect( m_UnifiedRB, SIGNAL(toggled(bool)), SLOT(updateCommandLine()) ); + connect( m_SideBySideRB, SIGNAL(toggled(bool)), SLOT(updateCommandLine()) ); + connect( m_ContextLinesSB, SIGNAL(valueChanged(int)), SLOT(updateCommandLine()) ); + connect( m_directoryRequester, SIGNAL(textChanged(const QString&)), SLOT(updateCommandLine()) ); + + m_FormatBG->setExclusive(true); + m_FormatBG->addButton(m_ContextRB, Kompare::Context); + m_FormatBG->addButton(m_EdRB, Kompare::Ed); + m_FormatBG->addButton(m_NormalRB, Kompare::Normal); + m_FormatBG->addButton(m_UnifiedRB, Kompare::Unified); + m_FormatBG->addButton(m_RCSRB, Kompare::RCS); + m_FormatBG->addButton(m_SideBySideRB, Kompare::SideBySide); + + loadOptions(); +} + +KompareSaveOptionsWidget::~KompareSaveOptionsWidget() +{ + +} + +QString KompareSaveOptionsWidget::directory() const +{ + return KUrl( m_directoryRequester->url() ).toLocalFile(); +} + +void KompareSaveOptionsWidget::updateCommandLine() +{ + QString cmdLine = "diff"; + + QString options = ""; + + switch( static_cast( m_FormatBG->checkedId() ) ) { + case Kompare::Unified : + cmdLine += " -U " + QString::number( m_ContextLinesSB->value() ); + break; + case Kompare::Context : + cmdLine += " -C " + QString::number( m_ContextLinesSB->value() ); + break; + case Kompare::RCS : + options += 'n'; + break; + case Kompare::Ed : + options += 'e'; + break; + case Kompare::SideBySide: + options += 'y'; + break; + case Kompare::Normal : + case Kompare::UnknownFormat : + default: + break; + } + + if ( m_SmallerChangesCB->isChecked() ) { + options += 'd'; + } + + if ( m_LargeFilesCB->isChecked() ) { + options += 'H'; + } + + if ( m_IgnoreCaseCB->isChecked() ){ + options += 'i'; + } + + if ( m_ExpandTabsCB->isChecked() ) { + options += 't'; + } + + if ( m_IgnoreEmptyLinesCB->isChecked() ) { + options += 'B'; + } + + if ( m_IgnoreWhiteSpaceCB->isChecked() ) { + options += 'b'; + } + + if ( m_FunctionNamesCB->isChecked() ) { + options += 'p'; + } + +// if ( ) { +// cmdLine += " -w"; +// } + + if ( m_RecursiveCB->isChecked() ) { + options += 'r'; + } + + if( m_NewFilesCB->isChecked() ) { + options += 'N'; + } + +// if( m_AllTextCB->isChecked() ) { +// options += 'a'; +// } + + if( options.length() > 0 ) { + cmdLine += " -" + options; + } + + cmdLine += " -- "; + cmdLine += constructRelativePath( m_directoryRequester->url().pathOrUrl(), m_source ); + cmdLine += ' '; + cmdLine += constructRelativePath( m_directoryRequester->url().pathOrUrl(), m_destination ); + + m_CommandLineLabel->setText( cmdLine ); +} + +void KompareSaveOptionsWidget::loadOptions() +{ + m_SmallerChangesCB->setChecked ( m_settings->m_createSmallerDiff ); + m_LargeFilesCB->setChecked ( m_settings->m_largeFiles ); + m_IgnoreCaseCB->setChecked ( m_settings->m_ignoreChangesInCase ); + m_ExpandTabsCB->setChecked ( m_settings->m_convertTabsToSpaces ); + m_IgnoreEmptyLinesCB->setChecked( m_settings->m_ignoreEmptyLines ); + m_IgnoreWhiteSpaceCB->setChecked( m_settings->m_ignoreWhiteSpace ); + m_FunctionNamesCB->setChecked ( m_settings->m_showCFunctionChange ); + m_RecursiveCB->setChecked ( m_settings->m_recursive ); + m_NewFilesCB->setChecked ( m_settings->m_newFiles ); +// m_AllTextCB->setChecked ( m_settings->m_allText ); + + m_ContextLinesSB->setValue ( m_settings->m_linesOfContext ); + + m_FormatBG->button(m_settings->m_format)->setChecked(true); + + updateCommandLine(); +} + +void KompareSaveOptionsWidget::saveOptions() +{ + m_settings->m_createSmallerDiff = m_SmallerChangesCB->isChecked(); + m_settings->m_largeFiles = m_LargeFilesCB->isChecked(); + m_settings->m_ignoreChangesInCase = m_IgnoreCaseCB->isChecked(); + m_settings->m_convertTabsToSpaces = m_ExpandTabsCB->isChecked(); + m_settings->m_ignoreEmptyLines = m_IgnoreEmptyLinesCB->isChecked(); + m_settings->m_ignoreWhiteSpace = m_IgnoreWhiteSpaceCB->isChecked(); + m_settings->m_showCFunctionChange = m_FunctionNamesCB->isChecked(); + m_settings->m_recursive = m_RecursiveCB->isChecked(); + m_settings->m_newFiles = m_NewFilesCB->isChecked(); +// m_settings->m_allText = m_AllTextCB->isChecked(); + + m_settings->m_linesOfContext = m_ContextLinesSB->value(); + + m_settings->m_format = static_cast( m_FormatBG->checkedId() ); + +} + +#include "komparesaveoptionswidget.moc" diff --git a/kompare/komparepart/komparesaveoptionswidget.h b/kompare/komparepart/komparesaveoptionswidget.h new file mode 100644 index 00000000..9c49815b --- /dev/null +++ b/kompare/komparepart/komparesaveoptionswidget.h @@ -0,0 +1,51 @@ +/*************************************************************************** + komparesaveoptionswidget.h + -------------------------- + begin : Sun Mar 4 2001 + Copyright 2001-2003 Otto Bruggeman + Copyright 2001-2003 John Firebaugh +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 KOMPARESAVEOPTIONSWIDGET_H +#define KOMPARESAVEOPTIONSWIDGET_H + +#include +#include + +#include "komparesaveoptionsbase.h" + +class DiffSettings; +class QButtonGroup; + +class KompareSaveOptionsWidget : public KompareSaveOptionsBase, public KompareFunctions +{ +Q_OBJECT +public: + KompareSaveOptionsWidget( QString source, QString destination, DiffSettings* settings, QWidget* parent ); + ~KompareSaveOptionsWidget(); + + void saveOptions(); + QString directory() const; + +protected slots: + void updateCommandLine(); + +private: + void loadOptions(); + + DiffSettings* m_settings; + QString m_source; + QString m_destination; + QButtonGroup* m_FormatBG; +}; + +#endif diff --git a/kompare/komparepart/komparesplitter.cpp b/kompare/komparepart/komparesplitter.cpp new file mode 100644 index 00000000..2848f881 --- /dev/null +++ b/kompare/komparepart/komparesplitter.cpp @@ -0,0 +1,492 @@ +/************************************************************************** +** komparesplitter.cpp +** ------------------- +** begin : Wed Jan 14 2004 +** Copyright 2004-2005 Jeff Snyder +** Copyright 2007-2011 Kevin Kofler +***************************************************************************/ + +/************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +***************************************************************************/ + +// associated header +#include "komparesplitter.h" + +// qt +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// kde +#include +#include +#include + +// kompare +#include "komparelistview.h" +#include "viewsettings.h" +#include "kompareconnectwidget.h" +#include "diffmodel.h" +#include "difference.h" + +using namespace Diff2; + +KompareSplitter::KompareSplitter( ViewSettings *settings, QWidget *parent ) : + QSplitter( Qt::Horizontal, parent ), + m_settings( settings ) +{ + QFrame *scrollFrame = static_cast(parent); + + // Set up the scrollFrame + scrollFrame->setFrameStyle( QFrame::NoFrame | QFrame::Plain ); + scrollFrame->setLineWidth(scrollFrame->style()->pixelMetric(QStyle::PM_DefaultFrameWidth)); + QGridLayout *pairlayout = new QGridLayout(scrollFrame); + pairlayout->setSpacing(0); + pairlayout->setContentsMargins( 0, 0, 0, 0 ); + m_vScroll = new QScrollBar( Qt::Vertical, scrollFrame ); + pairlayout->addWidget( m_vScroll, 0, 1 ); + m_hScroll = new QScrollBar( Qt::Horizontal, scrollFrame ); + pairlayout->addWidget( m_hScroll, 1, 0 ); + + new KompareListViewFrame(true, m_settings, this, "source"); + new KompareListViewFrame(false, m_settings, this, "destination"); + pairlayout->addWidget( this, 0, 0 ); + + // set up our looks + setLineWidth( style()->pixelMetric( QStyle::PM_DefaultFrameWidth ) ); + + setHandleWidth(50); + setChildrenCollapsible( false ); + setFrameStyle( QFrame::NoFrame ); + setSizePolicy( QSizePolicy (QSizePolicy::Ignored, QSizePolicy::Ignored )); + setOpaqueResize( true ); + setFocusPolicy( Qt::WheelFocus ); + + connect( this, SIGNAL(configChanged()), SLOT(slotConfigChanged()) ); + connect( this, SIGNAL(configChanged()), SLOT(slotDelayedRepaintHandles()) ); + connect( this, SIGNAL(configChanged()), SLOT(slotDelayedUpdateScrollBars()) ); + + // scrolling + connect( m_vScroll, SIGNAL(valueChanged(int)), SLOT(slotScrollToId(int)) ); + connect( m_vScroll, SIGNAL(sliderMoved(int)), SLOT(slotScrollToId(int)) ); + connect( m_hScroll, SIGNAL(valueChanged(int)), SIGNAL(setXOffset(int)) ); + connect( m_hScroll, SIGNAL(sliderMoved(int)), SIGNAL(setXOffset(int)) ); + + m_scrollTimer=new QTimer(this); + m_restartTimer = false; + connect (m_scrollTimer, SIGNAL(timeout()), SLOT(timerTimeout()) ); + + // we need to receive childEvents now so that d->list is ready for when + // slotSetSelection(...) arrives + kapp->sendPostedEvents(this, QEvent::ChildAdded); + + // init stuff + slotUpdateScrollBars(); +} + +KompareSplitter::~KompareSplitter() +{ +} + +QSplitterHandle* KompareSplitter::createHandle() +{ + return new KompareConnectWidgetFrame(m_settings, this); +} + +void KompareSplitter::slotDelayedRepaintHandles() +{ + QTimer::singleShot(0, this, SLOT(slotRepaintHandles())); +} + +void KompareSplitter::slotRepaintHandles() +{ + const int end = count(); + for ( int i = 1; i < end; ++i ) + handle(i)->update(); +} + +void KompareSplitter::timerTimeout() +{ + if ( m_restartTimer ) + m_restartTimer = false; + else + m_scrollTimer->stop(); + + slotDelayedRepaintHandles(); + + emit scrollViewsToId( m_scrollTo ); + slotRepaintHandles(); + m_vScroll->setValue( m_scrollTo ); +} + +void KompareSplitter::slotScrollToId( int id ) +{ + m_scrollTo = id; + + if( m_restartTimer ) + return; + + if( m_scrollTimer->isActive() ) + { + m_restartTimer = true; + } + else + { + emit scrollViewsToId( id ); + slotRepaintHandles(); + m_vScroll->setValue( id ); + m_scrollTimer->start( 30 ); + } +} + +void KompareSplitter::slotDelayedUpdateScrollBars() +{ + QTimer::singleShot( 0, this, SLOT( slotUpdateScrollBars() ) ); +} + +void KompareSplitter::slotUpdateScrollBars() +{ + const int end = count(); + for ( int i = 0; i < end; ++i ) { + KompareListView* lv = listView(i); + int minHScroll = minHScrollId(); + if (lv->contentsX() < minHScroll) { + lv->setXOffset(minHScroll); + } + } + + int m_scrollDistance = m_settings->m_scrollNoOfLines * lineHeight(); + int m_pageSize = pageSize(); + + if( needVScrollBar() ) + { + m_vScroll->show(); + + m_vScroll->blockSignals( true ); + m_vScroll->setRange( minVScrollId(), + maxVScrollId() ); + m_vScroll->setValue( scrollId() ); + m_vScroll->setSingleStep( m_scrollDistance ); + m_vScroll->setPageStep( m_pageSize ); + m_vScroll->blockSignals( false ); + } + else + { + m_vScroll->hide(); + } + + if( needHScrollBar() ) + { + m_hScroll->show(); + m_hScroll->blockSignals( true ); + m_hScroll->setRange( minHScrollId(), maxHScrollId() ); + m_hScroll->setValue( maxContentsX() ); + m_hScroll->setSingleStep( 10 ); + m_hScroll->setPageStep( minVisibleWidth() - 10 ); + m_hScroll->blockSignals( false ); + } + else + { + m_hScroll->hide(); + } +} + +void KompareSplitter::slotDelayedUpdateVScrollValue() +{ + QTimer::singleShot( 0, this, SLOT( slotUpdateVScrollValue() ) ); +} + +void KompareSplitter::slotUpdateVScrollValue() +{ + m_vScroll->setValue( scrollId() ); +} + +void KompareSplitter::keyPressEvent( QKeyEvent* e ) +{ + //keyboard scrolling + switch ( e->key() ) { + case Qt::Key_Right: + case Qt::Key_L: + m_hScroll->triggerAction( QAbstractSlider::SliderSingleStepAdd ); + break; + case Qt::Key_Left: + case Qt::Key_H: + m_hScroll->triggerAction( QAbstractSlider::SliderSingleStepSub ); + break; + case Qt::Key_Up: + case Qt::Key_K: + m_vScroll->triggerAction( QAbstractSlider::SliderSingleStepSub ); + break; + case Qt::Key_Down: + case Qt::Key_J: + m_vScroll->triggerAction( QAbstractSlider::SliderSingleStepAdd ); + break; + case Qt::Key_PageDown: + m_vScroll->triggerAction( QAbstractSlider::SliderPageStepAdd ); + break; + case Qt::Key_PageUp: + m_vScroll->triggerAction( QAbstractSlider::SliderPageStepSub ); + break; + } + e->accept(); + slotRepaintHandles(); +} + +void KompareSplitter::wheelEvent( QWheelEvent* e ) +{ + if ( e->orientation() == Qt::Vertical ) + { + if ( e->modifiers() & Qt::ControlModifier ) { + if ( e->delta() < 0 ) // scroll down one page + m_vScroll->triggerAction( QAbstractSlider::SliderPageStepAdd ); + else // scroll up one page + m_vScroll->triggerAction( QAbstractSlider::SliderPageStepSub ); + } else { + if ( e->delta() < 0 ) // scroll down + m_vScroll->triggerAction( QAbstractSlider::SliderSingleStepAdd ); + else // scroll up + m_vScroll->triggerAction( QAbstractSlider::SliderSingleStepSub ); + } + } + else + { + if ( e->modifiers() & Qt::ControlModifier ) { + if ( e->delta() < 0 ) // scroll right one page + m_hScroll->triggerAction( QAbstractSlider::SliderPageStepAdd ); + else // scroll left one page + m_hScroll->triggerAction( QAbstractSlider::SliderPageStepSub ); + } else { + if ( e->delta() < 0 ) // scroll to the right + m_hScroll->triggerAction( QAbstractSlider::SliderSingleStepAdd ); + else // scroll to the left + m_hScroll->triggerAction( QAbstractSlider::SliderSingleStepSub ); + } + } + e->accept(); + slotDelayedRepaintHandles(); +} + +/* FIXME: this should return/the scrollId() from the listview containing the + * /base/ of the diff. but there's bigger issues with that atm. + */ + +int KompareSplitter::scrollId() +{ + if(widget(0)) + return listView(0)->scrollId(); + return minVScrollId(); +} + +int KompareSplitter::lineHeight() +{ + if(widget(0)) + return listView(0)->fontMetrics().height(); + return 1; +} + +int KompareSplitter::pageSize() +{ + if(widget(0)) { + KompareListView *view = listView(0); + return view->visibleHeight() - view->style()->pixelMetric( QStyle::PM_ScrollBarExtent ); + } + return 1; +} + +bool KompareSplitter::needVScrollBar() +{ + int pagesize = pageSize(); + const int end = count(); + for ( int i = 0; i < end; ++i ) { + KompareListView *view = listView(i); + if( view ->contentsHeight() > pagesize) + return true; + } + return false; +} + +int KompareSplitter::minVScrollId() +{ + + int min = -1; + int mSId; + const int end = count(); + for ( int i = 0; i < end; ++i ) { + mSId = listView(i)->minScrollId(); + if (mSId < min || min == -1) + min = mSId; + } + return ( min == -1 ) ? 0 : min; +} + +int KompareSplitter::maxVScrollId() +{ + int max = 0; + int mSId; + const int end = count(); + for ( int i = 0; i < end; ++i ) { + mSId = listView(i)->maxScrollId(); + if ( mSId > max ) + max = mSId; + } + return max; +} + +bool KompareSplitter::needHScrollBar() +{ + const int end = count(); + for ( int i = 0; i < end; ++i ) { + KompareListView *view = listView(i); + if ( view->contentsWidth() > view->visibleWidth() ) + return true; + } + return false; +} + +int KompareSplitter::minHScrollId() +{ + // hardcode an offset to hide the tree controls + return 6; +} + +int KompareSplitter::maxHScrollId() +{ + int max = 0; + int mHSId; + const int end = count(); + for ( int i = 0; i < end; ++i ) { + KompareListView *view = listView(i); + mHSId = view->contentsWidth() - view->visibleWidth(); + if ( mHSId > max ) + max = mHSId; + } + return max; +} + +int KompareSplitter::maxContentsX() +{ + int max = 0; + int mCX; + const int end = count(); + for ( int i = 0; i < end; ++i ) { + mCX = listView(i)->contentsX(); + if ( mCX > max ) + max = mCX; + } + return max; +} + +int KompareSplitter::minVisibleWidth() +{ + // Why the hell do we want to know this? + // ah yes, it is because we use it to set the "page size" for horiz. scrolling. + // despite the fact that *none* has a pgright and pgleft key :P + // But we do have mousewheels with horizontal scrolling functionality, + // pressing shift and scrolling then goes left and right one page at the time + int min = -1; + int vW; + const int end = count(); + for ( int i = 0; i < end; ++i ) { + vW = listView(i)->visibleWidth(); + if ( vW < min || min == -1 ) + min = vW; + } + return ( min == -1 ) ? 0 : min; +} + +KompareListView* KompareSplitter::listView( int index ) +{ + return static_cast(widget(index))->view(); +} + +KompareConnectWidget* KompareSplitter::connectWidget( int index ) +{ + return static_cast(handle(index))->wid(); +} + +void KompareSplitter::slotSetSelection( const DiffModel* model, const Difference* diff ) +{ + const int end = count(); + for ( int i = 0; i < end; ++i ) { + connectWidget(i)->slotSetSelection( model, diff ); + listView(i)->slotSetSelection( model, diff ); + static_cast(widget(i))->slotSetModel( model ); + } + + slotDelayedRepaintHandles(); + slotDelayedUpdateScrollBars(); +} + +void KompareSplitter::slotSetSelection( const Difference* diff ) +{ + const int end = count(); + for ( int i = 0; i < end; ++i ) { + connectWidget(i)->slotSetSelection( diff ); + listView(i)->slotSetSelection( diff ); + } + + slotDelayedRepaintHandles(); + slotDelayedUpdateScrollBars(); +} + +void KompareSplitter::slotApplyDifference( bool apply ) +{ + const int end = count(); + for ( int i = 0; i < end; ++i ) + listView(i)->slotApplyDifference( apply ); + slotDelayedRepaintHandles(); +} + +void KompareSplitter::slotApplyAllDifferences( bool apply ) +{ + const int end = count(); + for ( int i = 0; i < end; ++i ) + listView(i)->slotApplyAllDifferences( apply ); + slotDelayedRepaintHandles(); + slotScrollToId( m_scrollTo ); // FIXME! +} + +void KompareSplitter::slotApplyDifference( const Difference* diff, bool apply ) +{ + const int end = count(); + for ( int i = 0; i < end; ++i ) + listView(i)->slotApplyDifference( diff, apply ); + slotDelayedRepaintHandles(); +} + +void KompareSplitter::slotDifferenceClicked( const Difference* diff ) +{ + const int end = count(); + for ( int i = 0; i < end; ++i ) + listView(i)->setSelectedDifference( diff, false ); + emit selectionChanged( diff ); +} + +void KompareSplitter::slotConfigChanged() +{ + const int end = count(); + for ( int i = 0; i < end; ++i ) { + KompareListView *view = listView(i); + view->setFont( m_settings->m_font ); + view->update(); + } +} +#include "komparesplitter.moc" diff --git a/kompare/komparepart/komparesplitter.h b/kompare/komparepart/komparesplitter.h new file mode 100644 index 00000000..11a344f2 --- /dev/null +++ b/kompare/komparepart/komparesplitter.h @@ -0,0 +1,121 @@ +/*************************************************************************** + komparesplitter.h + ----------------- + begin : Wed Jan 14 2004 + Copyright 2004 Jeff Snyder + Copyright 2007-2011 Kevin Kofler +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 _KOMPARESPLITTER_H_ +#define _KOMPARESPLITTER_H_ + +#include + +#include "komparemodellist.h" + +class QSplitterHandle; +class QTimer; +class QScrollBar; +class QWheelEvent; +class QKeyEvent; + +namespace Diff2 { +class DiffModel; +class Difference; +} +class ViewSettings; + +class KompareListView; +class KompareConnectWidget; + +class KompareSplitter : public QSplitter +{ + Q_OBJECT + +public: + KompareSplitter(ViewSettings *settings, QWidget *parent); + ~KompareSplitter(); + +signals: + void configChanged(); + + void scrollViewsToId( int id ); + void setXOffset( int x ); + + void selectionChanged( const Diff2::Difference* diff ); + +public slots: + void slotScrollToId( int id ); + void slotDelayedUpdateScrollBars(); + void slotUpdateScrollBars(); + void slotDelayedUpdateVScrollValue(); + void slotUpdateVScrollValue(); + void keyPressEvent( QKeyEvent* e ); + + void slotApplyDifference( bool apply ); + void slotApplyAllDifferences( bool apply ); + void slotApplyDifference( const Diff2::Difference* diff, bool apply ); + + void slotSetSelection( const Diff2::DiffModel* model, const Diff2::Difference* diff ); + void slotSetSelection( const Diff2::Difference* diff ); + + void slotDifferenceClicked( const Diff2::Difference* diff ); + + void slotConfigChanged(); + +protected: + void wheelEvent( QWheelEvent* e ); + + ViewSettings* settings() const { return m_settings; } + +protected slots: + void slotDelayedRepaintHandles(); + void slotRepaintHandles(); + void timerTimeout(); + +private: + // override from QSplitter + QSplitterHandle* createHandle(); + + void setCursor( int id, const QCursor& cursor ); + void unsetCursor( int id ); + +protected: + KompareListView* listView( int index ); + KompareConnectWidget* connectWidget( int index ); + +private: + + // Scrollbars. all this just for the goddamn scrollbars. i hate them. + int scrollId(); + int lineHeight(); + int pageSize(); + bool needVScrollBar(); + int minVScrollId(); + int maxVScrollId(); + bool needHScrollBar(); + int minHScrollId(); + int maxHScrollId(); + int maxContentsX(); + int minVisibleWidth(); + + QTimer* m_scrollTimer; + bool m_restartTimer; + int m_scrollTo; + + ViewSettings* m_settings; + QScrollBar* m_vScroll; + QScrollBar* m_hScroll; + + friend class KompareConnectWidgetFrame; +}; +#endif //_KOMPARESPLITTER_H_ diff --git a/kompare/komparepart/kompareview.cpp b/kompare/komparepart/kompareview.cpp new file mode 100644 index 00000000..e5d249e7 --- /dev/null +++ b/kompare/komparepart/kompareview.cpp @@ -0,0 +1,28 @@ +/************************************************************************** +** kompareview.cpp +** --------------- +** begin : Thu Nov 3 2011 +** Copyright 2011 Kevin Kofler +***************************************************************************/ + +/************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +***************************************************************************/ + +// associated header +#include "kompareview.h" + +// kompare +#include "komparesplitter.h" + +KompareView::KompareView(ViewSettings *settings, QWidget *parent) : + QFrame( parent ) +{ + setObjectName( "scrollFrame" ); + m_splitter = new KompareSplitter( settings, this ); +} diff --git a/kompare/komparepart/kompareview.h b/kompare/komparepart/kompareview.h new file mode 100644 index 00000000..93ea0644 --- /dev/null +++ b/kompare/komparepart/kompareview.h @@ -0,0 +1,39 @@ +/************************************************************************** +** kompareview.h +** ------------- +** begin : Thu Nov 3 2011 +** Copyright 2011 Kevin Kofler +***************************************************************************/ + +/************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 _KOMPAREVIEW_H_ +#define _KOMPAREVIEW_H_ + +#include + +#include "komparemodellist.h" + +class ViewSettings; +class KompareSplitter; + +class KompareView : public QFrame +{ + Q_OBJECT + +public: + KompareView(ViewSettings *settings, QWidget *parent); + ~KompareView() {} + KompareSplitter *splitter() { return m_splitter; } + +private: + KompareSplitter *m_splitter; +}; +#endif //_KOMPARESPLITTER_H_ diff --git a/kompare/kompareui.rc b/kompare/kompareui.rc new file mode 100644 index 00000000..cfcf64c8 --- /dev/null +++ b/kompare/kompareui.rc @@ -0,0 +1,33 @@ + + + + &File + + + + + + + + + + &Settings + + + + + + + + + + + + +Main Toolbar + + + + + + diff --git a/kompare/kompareurldialog.cpp b/kompare/kompareurldialog.cpp new file mode 100644 index 00000000..561dd451 --- /dev/null +++ b/kompare/kompareurldialog.cpp @@ -0,0 +1,190 @@ +/*************************************************************************** + kompareurldialog.cpp + -------------------- + begin : Sun Mar 4 2001 + Copyright 2001-2005,2009 Otto Bruggeman + Copyright 2001-2003 John Firebaugh + Copyright 2007 Kevin Kofler +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 "kompareurldialog.h" +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include "diffpage.h" +#include "diffsettings.h" +#include "filespage.h" +#include "filessettings.h" +#include "viewpage.h" +#include "viewsettings.h" + +KompareURLDialog::KompareURLDialog( QWidget *parent, Qt::WFlags flags ) + : KPageDialog( parent, flags ) +{ + setFaceType( List ); + KSharedConfig::Ptr cfg = KGlobal::config(); + + m_filesPage = new FilesPage(); + KPageWidgetItem *filesItem = addPage( m_filesPage, i18n( "Files" ) ); + filesItem->setIcon( KIcon( "text-plain" ) ); + filesItem->setHeader( i18n( "Here you can enter the files you want to compare." ) ); + m_filesSettings = new FilesSettings( this ); + m_filesSettings->loadSettings( cfg.data() ); + m_filesPage->setSettings( m_filesSettings ); + + m_diffPage = new DiffPage(); + KPageWidgetItem *diffItem = addPage( m_diffPage, i18n( "Diff" ) ); + diffItem->setIcon( KIcon( "text-x-patch" ) ); + diffItem->setHeader( i18n( "Here you can change the options for comparing the files." ) ); + m_diffSettings = new DiffSettings( this ); + m_diffSettings->loadSettings( cfg.data() ); + m_diffPage->setSettings( m_diffSettings ); + + m_viewPage = new ViewPage(); + KPageWidgetItem *viewItem = addPage( m_viewPage, i18n( "Appearance" ) ); + viewItem->setIcon( KIcon( "preferences-desktop-theme" ) ); + viewItem->setHeader( i18n( "Here you can change the options for the view." ) ); + m_viewSettings = new ViewSettings( this ); + m_viewSettings->loadSettings( cfg.data() ); + m_viewPage->setSettings( m_viewSettings ); + + adjustSize(); + + showButtonSeparator( true ); + + connect( m_filesPage->firstURLRequester(), SIGNAL( textChanged( const QString& ) ), + this, SLOT( slotEnableOk() ) ); + connect( m_filesPage->secondURLRequester(), SIGNAL( textChanged( const QString& ) ), + this, SLOT( slotEnableOk() ) ); + +} + +KompareURLDialog::~KompareURLDialog() +{ +} + +void KompareURLDialog::showEvent ( QShowEvent * event ) +{ + if ( !event->spontaneous () ) + { + slotEnableOk(); + } +} + +void KompareURLDialog::slotButtonClicked( int button ) +{ + if ( button == KDialog::Cancel ) + { + reject(); + return; + } + // BUG: 124121 File with filenames to be excluded does not exist so diff complains and no diffs are generated + kDebug(8102) << "Test to see if the file is an actual file that is given in the file with filenames to exclude field" << endl; + if ( m_diffPage->m_excludeFileNameGroupBox->isChecked() ) + { + kDebug(8102) << "Ok the checkbox is active..." << endl; + if ( QFileInfo( m_diffPage->m_excludeFileURLComboBox->currentText() ).isDir() ) + { + kDebug(8102) << "Don't enter directory names where filenames are expected..." << endl; + KMessageBox::sorry( this, i18n( "File used for excluding files cannot be found, please specify another file." ) ); + reject(); + return; + } + } + // Room for more checks for invalid input + + m_filesPage->setURLsInComboBoxes(); + + KSharedConfig::Ptr cfg = KGlobal::config(); + + m_filesPage->apply(); + m_diffPage->apply(); + m_viewPage->apply(); + + m_filesSettings->saveSettings( cfg.data() ); + m_diffSettings->saveSettings( cfg.data() ); + m_viewSettings->saveSettings( cfg.data() ); + + cfg->sync(); + + accept(); +} + +void KompareURLDialog::slotEnableOk() +{ + enableButtonOk( !m_filesPage->firstURLRequester()->url().isEmpty() && + !m_filesPage->secondURLRequester()->url().isEmpty() ); +} + +/** + * Returns the first URL, which was entered. + * @return first URL + */ +KUrl KompareURLDialog::getFirstURL() const +{ + return KUrl( m_filesPage->firstURLRequester()->url() ); +} + +/** + * Returns the second URL, which was entered. + * @return second URL + */ +KUrl KompareURLDialog::getSecondURL() const +{ + return KUrl( m_filesPage->secondURLRequester()->url() ); +} + +/** + * Returns the encoding. + * @return encoding + */ +QString KompareURLDialog::encoding() const +{ + return m_filesPage->encoding(); +} + +void KompareURLDialog::setFirstGroupBoxTitle( const QString& title ) +{ + m_filesPage->setFirstGroupBoxTitle( title ); +} + +void KompareURLDialog::setSecondGroupBoxTitle( const QString& title ) +{ + m_filesPage->setSecondGroupBoxTitle( title ); +} + +void KompareURLDialog::setGroup( const QString& groupName ) +{ + m_filesSettings->setGroup( groupName ); + m_filesSettings->loadSettings( KGlobal::config().data() ); + m_filesPage->setSettings( m_filesSettings ); +} + +void KompareURLDialog::setFirstURLRequesterMode( unsigned int mode ) +{ + m_filesPage->setFirstURLRequesterMode( mode ); +} + +void KompareURLDialog::setSecondURLRequesterMode( unsigned int mode ) +{ + m_filesPage->setSecondURLRequesterMode( mode ); +} + +#include "kompareurldialog.moc" + diff --git a/kompare/kompareurldialog.h b/kompare/kompareurldialog.h new file mode 100644 index 00000000..dc50c588 --- /dev/null +++ b/kompare/kompareurldialog.h @@ -0,0 +1,74 @@ +/*************************************************************************** + kcompareurldialog.h + ------------------- + begin : Sun Mar 4 2001 + Copyright 2001-2004 Otto Bruggeman + Copyright 2001-2003 John Firebaugh +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 KOMPAREURLDIALOG_H +#define KOMPAREURLDIALOG_H + +#include +#include + + + +class FilesPage; +class FilesSettings; +class DiffPage; +class DiffSettings; +class ViewPage; +class ViewSettings; + +/** + * Definition of class KompareURLDialog. + * @author Otto Bruggeman + * @author John Firebaugh + */ +class KompareURLDialog : public KPageDialog +{ + Q_OBJECT + +public: + explicit KompareURLDialog( QWidget *parent= 0, Qt::WFlags flags= 0 ); + ~KompareURLDialog(); + + KUrl getFirstURL() const; + KUrl getSecondURL() const; + QString encoding() const; + + void setFirstGroupBoxTitle ( const QString& title ); + void setSecondGroupBoxTitle( const QString& title ); + + void setGroup( const QString& groupName ); + + void setFirstURLRequesterMode ( unsigned int mode ); + void setSecondURLRequesterMode( unsigned int mode ); + +protected slots: + virtual void slotButtonClicked( int button ); + +private slots: + void slotEnableOk(); +protected: + void showEvent ( QShowEvent * event ); +private: + FilesPage* m_filesPage; + FilesSettings* m_filesSettings; + DiffPage* m_diffPage; + DiffSettings* m_diffSettings; + ViewPage* m_viewPage; + ViewSettings* m_viewSettings; +}; + +#endif diff --git a/kompare/kompareviewpart.desktop b/kompare/kompareviewpart.desktop new file mode 100644 index 00000000..5af4da28 --- /dev/null +++ b/kompare/kompareviewpart.desktop @@ -0,0 +1,4 @@ +[Desktop Entry] +Type=ServiceType +X-KDE-ServiceType=Kompare/ViewPart +X-KDE-Derived=KParts/ReadOnlyPart diff --git a/kompare/libdialogpages/CMakeLists.txt b/kompare/libdialogpages/CMakeLists.txt new file mode 100644 index 00000000..22906650 --- /dev/null +++ b/kompare/libdialogpages/CMakeLists.txt @@ -0,0 +1,23 @@ + +########### next target ############### + +set(dialogpages_PART_SRCS + filessettings.cpp + viewsettings.cpp + pagebase.cpp + diffpage.cpp + filespage.cpp + viewpage.cpp ) + + +kde4_add_library(komparedialogpages SHARED ${dialogpages_PART_SRCS}) + +include_directories(${LIBKOMPAREDIFF2_INCLUDE_DIR}) + +target_link_libraries(komparedialogpages ${KDE4_KDEUI_LIBS} ${KDE4_KIO_LIBS} ${LIBKOMPAREDIFF2_LIBRARIES}) + +set_target_properties(komparedialogpages PROPERTIES VERSION ${GENERIC_LIB_VERSION} +SOVERSION ${GENERIC_LIB_SOVERSION} ) + +install(TARGETS komparedialogpages ${INSTALL_TARGETS_DEFAULT_ARGS}) + diff --git a/kompare/libdialogpages/dialogpagesexport.h b/kompare/libdialogpages/dialogpagesexport.h new file mode 100644 index 00000000..b2de57f6 --- /dev/null +++ b/kompare/libdialogpages/dialogpagesexport.h @@ -0,0 +1,37 @@ +/*************************************************************************** + * Copyright 2007 Andreas Pakulat * + * Copyright 2006 Matt Rogers * + * Copyright 2004 Jarosław Staniek * + * * + * 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 LIBDIALOGPAGESEXPORT_H +#define LIBDIALOGPAGESEXPORT_H + +/* needed for KDE_EXPORT macros */ +#include + +#ifndef DIALOGPAGES_EXPORT +# ifdef MAKE_KOMPAREDIALOGPAGES_LIB +# define DIALOGPAGES_EXPORT KDE_EXPORT +# else +# define DIALOGPAGES_EXPORT KDE_IMPORT +# endif +#endif + +#endif + diff --git a/kompare/libdialogpages/diffpage.cpp b/kompare/libdialogpages/diffpage.cpp new file mode 100644 index 00000000..94221ca8 --- /dev/null +++ b/kompare/libdialogpages/diffpage.cpp @@ -0,0 +1,411 @@ +/*************************************************************************** + diffprefs.cpp + ------------- + begin : Sun Mar 4 2001 + Copyright 2001-2004 Otto Bruggeman + Copyright 2007-2011 Kevin Kofler +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 "diffpage.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 "diffsettings.h" + +DiffPage::DiffPage() : PageBase(), m_ignoreRegExpDialog( 0 ) +{ + m_tabWidget = new KTabWidget( this ); + + addDiffTab(); + + addFormatTab(); + + addOptionsTab(); + + addExcludeTab(); +} + +DiffPage::~DiffPage() +{ + m_settings = 0; +} + +void DiffPage::setSettings( DiffSettings* setts ) +{ + m_settings = setts; + + m_diffURLRequester->setUrl( m_settings->m_diffProgram ); + + m_newFilesCheckBox->setChecked ( m_settings->m_newFiles ); + m_smallerCheckBox->setChecked ( m_settings->m_createSmallerDiff ); + m_largerCheckBox->setChecked ( m_settings->m_largeFiles ); + m_tabsCheckBox->setChecked ( m_settings->m_convertTabsToSpaces ); + m_caseCheckBox->setChecked ( m_settings->m_ignoreChangesInCase ); + m_linesCheckBox->setChecked ( m_settings->m_ignoreEmptyLines ); + m_whitespaceCheckBox->setChecked ( m_settings->m_ignoreWhiteSpace ); + m_allWhitespaceCheckBox->setChecked ( m_settings->m_ignoreAllWhiteSpace ); + m_ignoreTabExpansionCheckBox->setChecked( m_settings->m_ignoreChangesDueToTabExpansion ); + + m_ignoreRegExpCheckBox->setChecked ( m_settings->m_ignoreRegExp ); + m_ignoreRegExpEdit->setCompletedItems( m_settings->m_ignoreRegExpTextHistory ); + m_ignoreRegExpEdit->setText ( m_settings->m_ignoreRegExpText ); + + m_locSpinBox->setValue( m_settings->m_linesOfContext ); + + m_modeButtonGroup->button( m_settings->m_format )->setChecked( true ); + + m_excludeFilePatternGroupBox->setChecked ( m_settings->m_excludeFilePattern ); + slotExcludeFilePatternToggled ( m_settings->m_excludeFilePattern ); + m_excludeFilePatternEditListBox->insertStringList( m_settings->m_excludeFilePatternList ); + + m_excludeFileNameGroupBox->setChecked( m_settings->m_excludeFilesFile ); + slotExcludeFileToggled ( m_settings->m_excludeFilesFile ); + m_excludeFileURLComboBox->setUrls( m_settings->m_excludeFilesFileHistoryList ); + m_excludeFileURLComboBox->setUrl ( KUrl( m_settings->m_excludeFilesFileURL ) ); +} + +DiffSettings* DiffPage::settings( void ) +{ + return m_settings; +} + +void DiffPage::restore() +{ + // this shouldn't do a thing... +} + +void DiffPage::apply() +{ + m_settings->m_diffProgram = m_diffURLRequester->url().pathOrUrl(); + + m_settings->m_newFiles = m_newFilesCheckBox->isChecked(); + m_settings->m_largeFiles = m_largerCheckBox->isChecked(); + m_settings->m_createSmallerDiff = m_smallerCheckBox->isChecked(); + m_settings->m_convertTabsToSpaces = m_tabsCheckBox->isChecked(); + m_settings->m_ignoreChangesInCase = m_caseCheckBox->isChecked(); + m_settings->m_ignoreEmptyLines = m_linesCheckBox->isChecked(); + m_settings->m_ignoreWhiteSpace = m_whitespaceCheckBox->isChecked(); + m_settings->m_ignoreAllWhiteSpace = m_allWhitespaceCheckBox->isChecked(); + m_settings->m_ignoreChangesDueToTabExpansion = m_ignoreTabExpansionCheckBox->isChecked(); + + m_settings->m_ignoreRegExp = m_ignoreRegExpCheckBox->isChecked(); + m_settings->m_ignoreRegExpText = m_ignoreRegExpEdit->text(); + m_settings->m_ignoreRegExpTextHistory = m_ignoreRegExpEdit->completionObject()->items(); + + m_settings->m_linesOfContext = m_locSpinBox->value(); + + m_settings->m_format = static_cast( m_modeButtonGroup->checkedId() ); + + m_settings->m_excludeFilePattern = m_excludeFilePatternGroupBox->isChecked(); + m_settings->m_excludeFilePatternList = m_excludeFilePatternEditListBox->items(); + + m_settings->m_excludeFilesFile = m_excludeFileNameGroupBox->isChecked(); + m_settings->m_excludeFilesFileURL = m_excludeFileURLComboBox->currentText(); + m_settings->m_excludeFilesFileHistoryList = m_excludeFileURLComboBox->urls(); + + m_settings->saveSettings( KGlobal::config().data() ); +} + +void DiffPage::setDefaults() +{ + m_diffURLRequester->setUrl( KUrl( "diff" ) ); + m_newFilesCheckBox->setChecked( true ); + m_smallerCheckBox->setChecked( true ); + m_largerCheckBox->setChecked( true ); + m_tabsCheckBox->setChecked( false ); + m_caseCheckBox->setChecked( false ); + m_linesCheckBox->setChecked( false ); + m_whitespaceCheckBox->setChecked( false ); + m_allWhitespaceCheckBox->setChecked( false ); + m_ignoreTabExpansionCheckBox->setChecked( false ); + m_ignoreRegExpCheckBox->setChecked( false ); + + m_ignoreRegExpEdit->setText( QString() ); + + m_locSpinBox->setValue( 3 ); + + m_modeButtonGroup->button( Kompare::Unified )->setChecked( true ); + + m_excludeFilePatternGroupBox->setChecked( false ); + + m_excludeFileNameGroupBox->setChecked( false ); +} + +void DiffPage::slotShowRegExpEditor() +{ + if ( ! m_ignoreRegExpDialog ) + m_ignoreRegExpDialog = KServiceTypeTrader::createInstanceFromQuery( "KRegExpEditor/KRegExpEditor", QString(), this ); + + KRegExpEditorInterface *iface = qobject_cast( m_ignoreRegExpDialog ); + + if ( !iface ) + return; + + iface->setRegExp( m_ignoreRegExpEdit->text() ); + bool ok = m_ignoreRegExpDialog->exec(); + + if ( ok ) + m_ignoreRegExpEdit->setText( iface->regExp() ); +} + +void DiffPage::slotExcludeFilePatternToggled( bool on ) +{ + m_excludeFilePatternEditListBox->setEnabled( on ); +} + +void DiffPage::slotExcludeFileToggled( bool on ) +{ + m_excludeFileURLComboBox->setEnabled( on ); + m_excludeFileURLRequester->setEnabled( on ); +} + +void DiffPage::addDiffTab() +{ + QWidget* page = new QWidget( this ); + QVBoxLayout* layout = new QVBoxLayout( page ); + layout->setSpacing( KDialog::spacingHint() ); + layout->setMargin( KDialog::marginHint() ); + + // add diff program selector + m_diffProgramGroup = new QGroupBox( page ); + layout->addWidget( m_diffProgramGroup ); + QVBoxLayout* bgLayout = new QVBoxLayout( m_diffProgramGroup ); + m_diffProgramGroup->setTitle( i18n( "Diff Program" ) ); + //m_diffProgramGroup->setMargin( KDialog::marginHint() ); + + m_diffURLRequester = new KUrlRequester( m_diffProgramGroup); + m_diffURLRequester->setObjectName("diffURLRequester" ); + m_diffURLRequester->setWhatsThis( i18n( "You can select a different diff program here. On Solaris the standard diff program does not support all the options that the GNU version does. This way you can select that version." ) ); + bgLayout->addWidget( m_diffURLRequester ); + + layout->addStretch( 1 ); + page->setMinimumSize( sizeHintForWidget( page ) ); + + m_tabWidget->addTab( page, i18n( "Diff" ) ); +} + +void DiffPage::addFormatTab() +{ + QWidget* page = new QWidget( this ); + QVBoxLayout* layout = new QVBoxLayout( page ); + layout->setSpacing( KDialog::spacingHint() ); + layout->setMargin( KDialog::marginHint() ); + + // add diff modes + m_modeButtonGroup = new QButtonGroup( page ); + QGroupBox *box = new QGroupBox( page ); + box->setWhatsThis( i18n( "Select the format of the output generated by diff. Unified is the one that is used most frequently because it is very readable. The KDE developers like this format the best so use it for sending patches." ) ); + layout->addWidget( box ); + QVBoxLayout* bgLayout = new QVBoxLayout( box ); + box->setTitle( i18n( "Output Format" ) ); + //m_modeButtonGroup->setMargin( KDialog::marginHint() ); + + QRadioButton *radioButton = new QRadioButton( i18n( "Context" ), box ); + m_modeButtonGroup->addButton( radioButton, Kompare::Context); + bgLayout->addWidget( radioButton ); + radioButton = new QRadioButton( i18n( "Normal" ), box ); + m_modeButtonGroup->addButton( radioButton, Kompare::Normal); + bgLayout->addWidget( radioButton ); + radioButton = new QRadioButton( i18n( "Unified" ), box ); + m_modeButtonGroup->addButton( radioButton, Kompare::Unified); + bgLayout->addWidget( radioButton ); + + // #lines of context (loc) + QGroupBox* groupBox = new QGroupBox( page ); + QHBoxLayout *groupLayout = new QHBoxLayout; + groupBox->setLayout( groupLayout ); + layout->addWidget( groupBox ); + groupBox->setTitle( i18n( "Lines of Context" ) ); + groupBox->setWhatsThis( i18n( "The number of context lines is normally 2 or 3. This makes the diff readable and applicable in most cases. More than 3 lines will only bloat the diff unnecessarily." ) ); + //groupBox->setMargin( KDialog::marginHint() ); + + QLabel* label = new QLabel( i18n( "Number of context lines:" )); + groupLayout->addWidget( label ); + label->setWhatsThis( i18n( "The number of context lines is normally 2 or 3. This makes the diff readable and applicable in most cases. More than 3 lines will only bloat the diff unnecessarily." ) ); + m_locSpinBox = new QSpinBox( groupBox ); + m_locSpinBox->setRange( 0, 100 ); + groupLayout->addWidget( m_locSpinBox ); + m_locSpinBox->setWhatsThis( i18n( "The number of context lines is normally 2 or 3. This makes the diff readable and applicable in most cases. More than 3 lines will only bloat the diff unnecessarily." ) ); + label->setBuddy( m_locSpinBox ); + + layout->addStretch( 1 ); + page->setMinimumSize( sizeHintForWidget( page ) ); + + m_tabWidget->addTab( page, i18n( "Format" ) ); +} + +void DiffPage::addOptionsTab() +{ + QWidget* page = new QWidget( this ); + QVBoxLayout* layout = new QVBoxLayout( page ); + layout->setSpacing( KDialog::spacingHint() ); + layout->setMargin( KDialog::marginHint() ); + + // add diff options + KButtonGroup* optionButtonGroup = new KButtonGroup( page ); + layout->addWidget( optionButtonGroup ); + QVBoxLayout* bgLayout = new QVBoxLayout( optionButtonGroup ); + optionButtonGroup->setTitle( i18n( "General" ) ); + //optionButtonGroup->setMargin( KDialog::marginHint() ); + + m_newFilesCheckBox = new QCheckBox( i18n( "&Treat new files as empty" ), optionButtonGroup ); + m_newFilesCheckBox->setToolTip( i18n( "This option corresponds to the -N diff option." ) ); + m_newFilesCheckBox->setWhatsThis( i18n( "With this option enabled diff will treat a file that only exists in one of the directories as empty in the other directory. This means that the file is compared with an empty file and because of this will appear as one big insertion or deletion." ) ); + bgLayout->addWidget( m_newFilesCheckBox ); + + m_smallerCheckBox = new QCheckBox( i18n( "&Look for smaller changes" ), optionButtonGroup ); + m_smallerCheckBox->setToolTip( i18n( "This corresponds to the -d diff option." ) ); + m_smallerCheckBox->setWhatsThis( i18n( "With this option enabled diff will try a little harder (at the cost of more memory) to find fewer changes." ) ); + bgLayout->addWidget( m_smallerCheckBox ); + m_largerCheckBox = new QCheckBox( i18n( "O&ptimize for large files" ), optionButtonGroup ); + m_largerCheckBox->setToolTip( i18n( "This corresponds to the -H diff option." ) ); + m_largerCheckBox->setWhatsThis( i18n( "This option lets diff makes better diffs when using large files. The definition of large is nowhere to be found though." ) ); + bgLayout->addWidget( m_largerCheckBox ); + m_caseCheckBox = new QCheckBox( i18n( "&Ignore changes in case" ), optionButtonGroup ); + m_caseCheckBox->setToolTip( i18n( "This corresponds to the -i diff option." ) ); + m_caseCheckBox->setWhatsThis( i18n( "With this option to ignore changes in case enabled, diff will not indicate a difference when something in one file is changed into SoMEthing in the other file." ) ); + bgLayout->addWidget( m_caseCheckBox ); + + QHBoxLayout* groupLayout = new QHBoxLayout(); + layout->addLayout( groupLayout ); + groupLayout->setObjectName( "regexp_horizontal_layout" ); + groupLayout->setSpacing( -1 ); + groupLayout->setMargin( KDialog::marginHint() ); + + m_ignoreRegExpCheckBox = new QCheckBox( i18n( "Ignore regexp:" ), page ); + m_ignoreRegExpCheckBox->setToolTip( i18n( "This option corresponds to the -I diff option." ) ); + m_ignoreRegExpCheckBox->setWhatsThis( i18n( "When this checkbox is enabled, an option to diff is given that will make diff ignore lines that match the regular expression." ) ); + groupLayout->addWidget( m_ignoreRegExpCheckBox ); + m_ignoreRegExpEdit = new KLineEdit( QString::null, page); //krazy:exclude=nullstrassign for old broken gcc + m_ignoreRegExpEdit->setObjectName("regexplineedit" ); + m_ignoreRegExpEdit->setToolTip( i18n( "Add the regular expression here that you want to use\nto ignore lines that match it." ) ); + groupLayout->addWidget( m_ignoreRegExpEdit ); + + if ( !KServiceTypeTrader::self()->query("KRegExpEditor/KRegExpEditor").isEmpty() ) + { + // Ok editor is available, use it + QPushButton* ignoreRegExpEditButton = new QPushButton( i18n( "&Edit..." ), page); + ignoreRegExpEditButton->setObjectName( "regexp_editor_button" ); + ignoreRegExpEditButton->setToolTip( i18n( "Clicking this will open a regular expression dialog where\nyou can graphically create regular expressions." ) ); + groupLayout->addWidget( ignoreRegExpEditButton ); + connect( ignoreRegExpEditButton, SIGNAL( clicked() ), this, SLOT( slotShowRegExpEditor() ) ); + } + + KButtonGroup* moreOptionButtonGroup = new KButtonGroup( page ); + layout->addWidget( moreOptionButtonGroup ); + bgLayout = new QVBoxLayout( moreOptionButtonGroup ); + moreOptionButtonGroup->setTitle( i18n( "Whitespace" ) ); + //moreOptionButtonGroup->setMargin( KDialog::marginHint() ); + + m_tabsCheckBox = new QCheckBox( i18n( "E&xpand tabs to spaces in output" ), moreOptionButtonGroup ); + m_tabsCheckBox->setToolTip( i18n( "This option corresponds to the -t diff option." ) ); + m_tabsCheckBox->setWhatsThis( i18n( "This option does not always produce the right result. Due to this expansion Kompare may have problems applying the change to the destination file." ) ); + bgLayout->addWidget( m_tabsCheckBox ); + m_linesCheckBox = new QCheckBox( i18n( "I&gnore added or removed empty lines" ), moreOptionButtonGroup ); + m_linesCheckBox->setToolTip( i18n( "This option corresponds to the -B diff option." ) ); + m_linesCheckBox->setWhatsThis( i18n( "This can be very useful in situations where code has been reorganized and empty lines have been added or removed to improve legibility." ) ); + bgLayout->addWidget( m_linesCheckBox ); + m_whitespaceCheckBox = new QCheckBox( i18n( "Ig&nore changes in the amount of whitespace" ), moreOptionButtonGroup ); + m_whitespaceCheckBox->setToolTip( i18n( "This option corresponds to the -b diff option." ) ); + m_whitespaceCheckBox->setWhatsThis( i18n( "If you are uninterested in differences arising due to, for example, changes in indentation, then use this option." ) ); + bgLayout->addWidget( m_whitespaceCheckBox ); + m_allWhitespaceCheckBox = new QCheckBox( i18n( "Ign&ore all whitespace" ), moreOptionButtonGroup ); + m_allWhitespaceCheckBox->setToolTip( i18n( "This option corresponds to the -w diff option." ) ); + m_allWhitespaceCheckBox->setWhatsThis( i18n( "This is useful for seeing the significant changes without being overwhelmed by all the white space changes." ) ); + bgLayout->addWidget( m_allWhitespaceCheckBox ); + m_ignoreTabExpansionCheckBox = new QCheckBox( i18n( "Igno&re changes due to tab expansion" ), moreOptionButtonGroup ); + m_ignoreTabExpansionCheckBox->setToolTip( i18n( "This option corresponds to the -E diff option." ) ); + m_ignoreTabExpansionCheckBox->setWhatsThis( i18n( "If there is a change because tabs have been expanded into spaces in the other file, then this option will make sure that these do not show up. Kompare currently has some problems applying such changes so be careful when you use this option." ) ); + bgLayout->addWidget( m_ignoreTabExpansionCheckBox ); + + layout->addStretch( 1 ); + page->setMinimumSize( sizeHintForWidget( page ) ); + + m_tabWidget->addTab( page, i18n( "Options" ) ); +} + +void DiffPage::addExcludeTab() +{ + QWidget* page = new QWidget( this ); + QVBoxLayout* layout = new QVBoxLayout( page ); + layout->setSpacing( KDialog::spacingHint() ); + layout->setMargin( KDialog::marginHint() ); + + m_excludeFilePatternGroupBox = new QGroupBox( page ); + m_excludeFilePatternGroupBox->setCheckable(true); + QHBoxLayout *excludeFileLayout = new QHBoxLayout; + m_excludeFilePatternGroupBox->setLayout( excludeFileLayout ); + m_excludeFilePatternGroupBox->setTitle( i18n( "File Pattern to Exclude" ) ); + m_excludeFilePatternGroupBox->setToolTip( i18n( "If this is checked you can enter a shell pattern in the text box on the right or select entries from the list." ) ); + m_excludeFilePatternEditListBox = new KEditListWidget; + excludeFileLayout->addWidget( m_excludeFilePatternEditListBox ); + m_excludeFilePatternEditListBox->setObjectName( "exclude_file_pattern_editlistbox" ); + m_excludeFilePatternEditListBox->setButtons( KEditListWidget::Add|KEditListWidget::Remove ); + m_excludeFilePatternEditListBox->setCheckAtEntering( false ); + m_excludeFilePatternEditListBox->setToolTip( i18n( "Here you can enter or remove a shell pattern or select one or more entries from the list." ) ); + layout->addWidget( m_excludeFilePatternGroupBox ); + + + connect( m_excludeFilePatternGroupBox, SIGNAL(toggled(bool)), this, SLOT(slotExcludeFilePatternToggled(bool))); + + m_excludeFileNameGroupBox = new QGroupBox( page ); + m_excludeFileNameGroupBox->setCheckable( true ); + excludeFileLayout = new QHBoxLayout; + m_excludeFileNameGroupBox->setLayout( excludeFileLayout ); + m_excludeFileNameGroupBox->setTitle( i18n( "File with Filenames to Exclude" ) ); + m_excludeFileNameGroupBox->setToolTip( i18n( "If this is checked you can enter a filename in the combo box below." ) ); + m_excludeFileURLComboBox = new KUrlComboBox( KUrlComboBox::Files, true ); + excludeFileLayout->addWidget( m_excludeFileURLComboBox ); + m_excludeFileURLComboBox->setObjectName( "exclude_file_urlcombo" ); + m_excludeFileURLComboBox->setToolTip( i18n( "Here you can enter the URL of a file with shell patterns to ignore during the comparison of the folders." ) ); + m_excludeFileURLRequester = new KUrlRequester( m_excludeFileURLComboBox,m_excludeFileNameGroupBox ); + excludeFileLayout->addWidget( m_excludeFileURLRequester ); + m_excludeFileURLRequester->setObjectName("exclude_file_name_urlrequester" ); + m_excludeFileURLRequester->setToolTip( i18n( "Any file you select in the dialog that pops up when you click it will be put in the dialog to the left of this button." ) ); + layout->addWidget( m_excludeFileNameGroupBox ); + + connect( m_excludeFileNameGroupBox, SIGNAL(toggled(bool)), this, SLOT(slotExcludeFileToggled(bool))); + + layout->addStretch( 1 ); + page->setMinimumSize( sizeHintForWidget( page ) ); + + m_tabWidget->addTab( page, i18n( "Exclude" ) ); +} + +#include "diffpage.moc" + +//kate: replace-tabs 0; indent-width 4; tab-width 4; diff --git a/kompare/libdialogpages/diffpage.h b/kompare/libdialogpages/diffpage.h new file mode 100644 index 00000000..37490b1e --- /dev/null +++ b/kompare/libdialogpages/diffpage.h @@ -0,0 +1,103 @@ +/*************************************************************************** + diffprefs.h + ----------- + begin : Sun Mar 4 2001 + Copyright 2001-2004 Otto Bruggeman + Copyright 2001-2003 John Firebaugh + Copyright 2007 Kevin Kofler +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 DIFFPAGE_H +#define DIFFPAGE_H + +#include "pagebase.h" +#include "dialogpagesexport.h" + +class QCheckBox; +class QDialog; +class QSpinBox; +class QStringList; +class QButtonGroup; +class QGroupBox; + +class KLineEdit; +class KEditListWidget; +class KTabWidget; +class KUrlComboBox; +class KUrlRequester; + +class DiffSettings; + +class DIALOGPAGES_EXPORT DiffPage : public PageBase +{ +Q_OBJECT +public: + DiffPage(); + ~DiffPage(); + +public: + void setSettings( DiffSettings* ); + DiffSettings* settings( void ); + +public: + virtual void restore(); + virtual void apply(); + virtual void setDefaults(); + +protected slots: + void slotShowRegExpEditor(); + void slotExcludeFilePatternToggled( bool ); + void slotExcludeFileToggled( bool ); + +private: + void addDiffTab(); + void addFormatTab(); + void addOptionsTab(); + void addExcludeTab(); + +public: + DiffSettings* m_settings; + + KUrlRequester* m_diffURLRequester; + + QCheckBox* m_newFilesCheckBox; + QCheckBox* m_smallerCheckBox; + QCheckBox* m_largerCheckBox; + QCheckBox* m_tabsCheckBox; + QCheckBox* m_caseCheckBox; + QCheckBox* m_linesCheckBox; + QCheckBox* m_whitespaceCheckBox; + QCheckBox* m_allWhitespaceCheckBox; + QCheckBox* m_ignoreTabExpansionCheckBox; + + QCheckBox* m_ignoreRegExpCheckBox; + KLineEdit* m_ignoreRegExpEdit; + QStringList* m_ignoreRegExpEditHistory; + QDialog* m_ignoreRegExpDialog; + + QGroupBox* m_excludeFilePatternGroupBox; + KEditListWidget* m_excludeFilePatternEditListBox; + + QGroupBox* m_excludeFileNameGroupBox; + KUrlComboBox* m_excludeFileURLComboBox; + KUrlRequester* m_excludeFileURLRequester; + + // loc == lines of context + QSpinBox* m_locSpinBox; + + QButtonGroup* m_modeButtonGroup; + QGroupBox* m_diffProgramGroup; + + KTabWidget* m_tabWidget; +}; + +#endif diff --git a/kompare/libdialogpages/filespage.cpp b/kompare/libdialogpages/filespage.cpp new file mode 100644 index 00000000..417fbd12 --- /dev/null +++ b/kompare/libdialogpages/filespage.cpp @@ -0,0 +1,162 @@ +/*************************************************************************** + filespage.cpp + ------------- + begin : Sun Apr 18 2004 + Copyright 2004 Otto Bruggeman + Copyright 2007-2011 Kevin Kofler +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 "filespage.h" + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "filessettings.h" + +FilesPage::FilesPage() : PageBase() +{ + QWidget* page = new QWidget( this ); + QVBoxLayout* layout = new QVBoxLayout( page ); + layout->setSpacing( KDialog::spacingHint() ); + layout->setMargin( KDialog::marginHint() ); + + m_firstGB = new QGroupBox( "You have to set this moron :)", page ); + layout->addWidget( m_firstGB ); + QHBoxLayout* gb1Layout = new QHBoxLayout( m_firstGB ); + m_firstURLComboBox = new KUrlComboBox( KUrlComboBox::Both, true, m_firstGB ); + m_firstURLComboBox->setObjectName( "SourceURLComboBox" ); + m_firstURLRequester = new KUrlRequester( m_firstURLComboBox, m_firstGB ); + gb1Layout->addWidget( m_firstURLRequester ); + m_firstURLRequester->setFocus(); + + m_secondGB = new QGroupBox( "This too moron !", page ); + layout->addWidget( m_secondGB ); + QHBoxLayout* gb2Layout = new QHBoxLayout( m_secondGB ); + m_secondURLComboBox = new KUrlComboBox( KUrlComboBox::Both, true, m_secondGB ); + m_secondURLComboBox->setObjectName( "DestURLComboBox" ); + m_secondURLRequester = new KUrlRequester( m_secondURLComboBox, m_secondGB ); + gb2Layout->addWidget( m_secondURLRequester ); + + m_thirdGB = new QGroupBox( i18n( "Encoding" ), page ); + layout->addWidget( m_thirdGB ); + QHBoxLayout* gb3Layout = new QHBoxLayout( m_thirdGB ); + m_encodingComboBox = new KComboBox( false, m_thirdGB ); + m_encodingComboBox->setObjectName( "encoding_combobox" ); + m_encodingComboBox->insertItem( 0, "Default" ); + m_encodingComboBox->insertItems( 1, KGlobal::charsets()->availableEncodingNames() ); + gb3Layout->addWidget( m_encodingComboBox ); + + layout->addWidget( m_firstGB ); + layout->addWidget( m_secondGB ); + layout->addWidget( m_thirdGB ); + + layout->addStretch( 1 ); + page->setMinimumSize( sizeHintForWidget( page ) ); + + //addTab( page, i18n( "&Files" ) ); +} + +FilesPage::~FilesPage() +{ + m_settings = 0; +} + +KUrlRequester* FilesPage::firstURLRequester() const +{ + return m_firstURLRequester; +} + +KUrlRequester* FilesPage::secondURLRequester() const +{ + return m_secondURLRequester; +} + +QString FilesPage::encoding() const +{ + return m_encodingComboBox->currentText(); +} + +void FilesPage::setFirstGroupBoxTitle( const QString& title ) +{ + m_firstGB->setTitle( title ); +} + +void FilesPage::setSecondGroupBoxTitle( const QString& title ) +{ + m_secondGB->setTitle( title ); +} + +void FilesPage::setURLsInComboBoxes() +{ +// kDebug() << "first : " << m_firstURLComboBox->currentText() << endl; +// kDebug() << "second: " << m_secondURLComboBox->currentText() << endl; + m_firstURLComboBox->setUrl( KUrl( m_firstURLComboBox->currentText() ) ); + m_secondURLComboBox->setUrl( KUrl( m_secondURLComboBox->currentText() ) ); +} + + +void FilesPage::setFirstURLRequesterMode( unsigned int mode ) +{ + m_firstURLRequester->setMode( (KFile::Mode) mode ); +} + +void FilesPage::setSecondURLRequesterMode( unsigned int mode ) +{ + m_secondURLRequester->setMode( (KFile::Mode) mode ); +} + +void FilesPage::setSettings( FilesSettings* settings ) +{ + m_settings = settings; + + m_firstURLComboBox->setUrls( m_settings->m_recentSources ); + m_firstURLComboBox->setUrl( KUrl( m_settings->m_lastChosenSourceURL ) ); + m_secondURLComboBox->setUrls( m_settings->m_recentDestinations ); + m_secondURLComboBox->setUrl( KUrl( m_settings->m_lastChosenDestinationURL ) ); + m_encodingComboBox->setCurrentIndex( m_encodingComboBox->findText( m_settings->m_encoding, Qt::MatchFixedString ) ); +} + +void FilesPage::restore() +{ + // this shouldn't do a thing... +} + +void FilesPage::apply() +{ + m_settings->m_recentSources = m_firstURLComboBox->urls(); + m_settings->m_lastChosenSourceURL = m_firstURLComboBox->currentText(); + m_settings->m_recentDestinations = m_secondURLComboBox->urls(); + m_settings->m_lastChosenDestinationURL = m_secondURLComboBox->currentText(); + m_settings->m_encoding = m_encodingComboBox->currentText(); +} + +void FilesPage::setDefaults() +{ + m_firstURLComboBox->setUrls( QStringList() ); + m_firstURLComboBox->setUrl( KUrl( "" ) ); + m_secondURLComboBox->setUrls( QStringList() ); + m_secondURLComboBox->setUrl( KUrl( "" ) ); + m_encodingComboBox->setCurrentIndex( 0 ); // "Default" +} + +#include "filespage.moc" diff --git a/kompare/libdialogpages/filespage.h b/kompare/libdialogpages/filespage.h new file mode 100644 index 00000000..42afafcd --- /dev/null +++ b/kompare/libdialogpages/filespage.h @@ -0,0 +1,76 @@ +/*************************************************************************** + kcompareurldialog.h + ------------------- + begin : Sun Mar 4 2001 + Copyright 2001-2004 Otto Bruggeman + Copyright 2001-2003 John Firebaugh + Copyright 2007 Kevin Kofler +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 FILESPAGE_H +#define FILESPAGE_H + +#include "pagebase.h" +#include "dialogpagesexport.h" + +class QGroupBox; + +class KComboBox; +class KUrlComboBox; +class KUrlRequester; + +class FilesSettings; + +class DIALOGPAGES_EXPORT FilesPage : public PageBase +{ +Q_OBJECT +public: + FilesPage(); + virtual ~FilesPage(); + +public: + KUrlRequester* firstURLRequester() const; + KUrlRequester* secondURLRequester() const; + + QString encoding() const; + + void setFirstGroupBoxTitle ( const QString& title ); + void setSecondGroupBoxTitle( const QString& title ); + + void setURLsInComboBoxes(); + + void setFirstURLRequesterMode( unsigned int mode ); + void setSecondURLRequesterMode( unsigned int mode ); + +public: + virtual void setSettings( FilesSettings* settings ); + virtual void restore(); + virtual void apply(); + virtual void setDefaults(); + +private: + QGroupBox* m_firstGB; + QGroupBox* m_secondGB; + QGroupBox* m_thirdGB; + KUrlComboBox* m_firstURLComboBox; + KUrlComboBox* m_secondURLComboBox; + KUrlRequester* m_firstURLRequester; + KUrlRequester* m_secondURLRequester; + // Use this bool to lock the connection between both KUrlRequesters. + // This prevents annoying behaviour + bool m_URLChanged; + KComboBox* m_encodingComboBox; + + FilesSettings* m_settings; +}; + +#endif diff --git a/kompare/libdialogpages/filessettings.cpp b/kompare/libdialogpages/filessettings.cpp new file mode 100644 index 00000000..0e19dc00 --- /dev/null +++ b/kompare/libdialogpages/filessettings.cpp @@ -0,0 +1,60 @@ +/*************************************************************************** + filessettings.cpp + ----------------- + begin : Sun Apr 18 2004 + Copyright 2004 Otto Bruggeman + Copyright 2007 Kevin Kofler +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 "filessettings.h" + +#include +#include +#include + +FilesSettings::FilesSettings( QWidget* parent ) + : SettingsBase( parent ) +{ +} + +FilesSettings::~FilesSettings() +{ +} + +void FilesSettings::loadSettings( KConfig* config ) +{ + KConfigGroup group( config, m_configGroupName ); + + m_recentSources = group.readEntry( "Recent Sources", QStringList() ); + m_lastChosenSourceURL = group.readEntry ( "LastChosenSourceListEntry", "" ); + m_recentDestinations = group.readEntry( "Recent Destinations", QStringList() ); + m_lastChosenDestinationURL = group.readEntry ( "LastChosenDestinationListEntry", "" ); + m_encoding = group.readEntry ( "Encoding", "default" ); +} + +void FilesSettings::saveSettings( KConfig* config ) +{ + KConfigGroup group( config, m_configGroupName ); + group.writeEntry( "Recent Sources", m_recentSources ); + group.writeEntry( "Recent Destinations", m_recentDestinations ); + group.writeEntry( "LastChosenSourceListEntry", m_lastChosenSourceURL ); + group.writeEntry( "LastChosenDestinationListEntry", m_lastChosenDestinationURL ); + group.writeEntry( "Encoding", m_encoding ); + config->sync(); +} + +void FilesSettings::setGroup( const QString& groupName ) +{ + m_configGroupName = groupName; +} + +#include "filessettings.moc" diff --git a/kompare/libdialogpages/filessettings.h b/kompare/libdialogpages/filessettings.h new file mode 100644 index 00000000..dc3306e3 --- /dev/null +++ b/kompare/libdialogpages/filessettings.h @@ -0,0 +1,54 @@ +/*************************************************************************** + filessettings.h + --------------- + begin : Sun Apr 18 2004 + Copyright 2004 Otto Bruggeman +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 FILESSETTINGS_H +#define FILESSETTINGS_H + +#include +#include + +#include + +#include "dialogpagesexport.h" + +class KConfig; + +class DIALOGPAGES_EXPORT FilesSettings : public SettingsBase +{ +Q_OBJECT +public: + FilesSettings( QWidget* parent ); + virtual ~FilesSettings(); + +public: + // some virtual functions that will be overloaded from the base class + virtual void loadSettings( KConfig* config ); + virtual void saveSettings( KConfig* config ); + + void setGroup( const QString& groupName ); + +public: + QString m_configGroupName; + + QStringList m_recentSources; + QString m_lastChosenSourceURL; + QStringList m_recentDestinations; + QString m_lastChosenDestinationURL; + QString m_encoding; +}; + +#endif // FILESSETTINGS_H + diff --git a/kompare/libdialogpages/pagebase.cpp b/kompare/libdialogpages/pagebase.cpp new file mode 100644 index 00000000..4aa33d7d --- /dev/null +++ b/kompare/libdialogpages/pagebase.cpp @@ -0,0 +1,105 @@ +/*************************************************************************** + prefsbase.cpp + ------------- + begin : Sun Mar 4 2001 + Copyright 2001 Otto Bruggeman + Copyright 2001 John Firebaugh + Copyright 2007 Kevin Kofler +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 "pagebase.h" + +#include +#include + +PageBase::PageBase() : KVBox() +{ + +} + +PageBase::~PageBase() +{ + +} + +/** No descriptions */ +QSize PageBase::sizeHintForWidget( QWidget* widget ) +{ + // + // The size is computed by adding the sizeHint().height() of all + // widget children and taking the width of the widest child and adding + // layout()->margin() and layout()->spacing() + // + + // this code in this method has been ripped out of a file in kbabel + // so copyright goes to the kbabel authors. + + QSize size; + + int numChild = 0; + QList l = widget->children(); + + + + for( int i=0; i < l.count(); i++ ) + { + QObject *o = l.at(i); + if( o->isWidgetType() ) + { + numChild += 1; + QWidget *w=((QWidget*)o); + + QSize s = w->sizeHint(); + if( s.isEmpty() == true ) + { + s = QSize( 50, 100 ); // Default size + } + size.setHeight( size.height() + s.height() ); + if( s.width() > size.width() ) + { + size.setWidth( s.width() ); + } + } + } + + if( numChild > 0 ) + { + size.setHeight( size.height() + widget->layout()->spacing()*(numChild-1) ); + size += QSize( widget->layout()->margin()*2, widget->layout()->margin()*2 + 1 ); + } + else + { + size = QSize( 1, 1 ); + } + + return( size ); +} + +/** No descriptions */ +void PageBase::apply() +{ + +} + +/** No descriptions */ +void PageBase::restore() +{ + +} + +/** No descriptions */ +void PageBase::setDefaults() +{ + +} + +#include "pagebase.moc" diff --git a/kompare/libdialogpages/pagebase.h b/kompare/libdialogpages/pagebase.h new file mode 100644 index 00000000..0cef46fe --- /dev/null +++ b/kompare/libdialogpages/pagebase.h @@ -0,0 +1,45 @@ +/*************************************************************************** + prefsbase.h + ----------- + begin : Sun Mar 4 2001 + Copyright 2001 Otto Bruggeman + Copyright 2001 John Firebaugh + Copyright 2007 Kevin Kofler +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 PAGEBASE_H +#define PAGEBASE_H + +#include + +#include +#include + +class PageBase : public KVBox +{ +Q_OBJECT +public: + PageBase(); + ~PageBase(); + +public: + /** No descriptions */ + QSize sizeHintForWidget( QWidget* widget ); + /** No descriptions */ + virtual void restore(); + /** No descriptions */ + virtual void apply(); + /** No descriptions */ + virtual void setDefaults(); +}; + +#endif diff --git a/kompare/libdialogpages/viewpage.cpp b/kompare/libdialogpages/viewpage.cpp new file mode 100644 index 00000000..e26d7284 --- /dev/null +++ b/kompare/libdialogpages/viewpage.cpp @@ -0,0 +1,216 @@ +/*************************************************************************** + viewprefs.cpp + ------------- + begin : Sun Mar 4 2001 + Copyright 2001-2002 Otto Bruggeman + Copyright 2001-2002 John Firebaugh + Copyright 2007-2011 Kevin Kofler +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 "viewpage.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "viewsettings.h" + +ViewPage::ViewPage() : PageBase() +{ + QWidget* page; + QVBoxLayout* layout; + QGridLayout* gridLayout; + QGroupBox* colorGroupBox; + QGroupBox* snolGroupBox; + QGroupBox* tabGroupBox; + QLabel* label; + + m_tabWidget = new KTabWidget( this ); + page = new QWidget( m_tabWidget ); + layout = new QVBoxLayout( page ); + layout->setSpacing( KDialog::spacingHint() ); + layout->setMargin( KDialog::marginHint() ); + + // add a groupbox + colorGroupBox = new QGroupBox( page ); + colorGroupBox->setTitle( i18n( "Colors" ) ); + layout->addWidget( colorGroupBox ); + //colorGroupBox->setMargin( KDialog::marginHint() ); + gridLayout = new QGridLayout( colorGroupBox ); + + // add the removeColor + label = new QLabel( i18n( "Removed color:" ), colorGroupBox ); + m_removedColorButton = new KColorButton( colorGroupBox ); + label->setBuddy( m_removedColorButton ); + gridLayout->addWidget( label, 0, 0 ); + gridLayout->addWidget( m_removedColorButton, 0, 1 ); + + // add the changeColor + label = new QLabel( i18n( "Changed color:" ), colorGroupBox ); + m_changedColorButton = new KColorButton( colorGroupBox ); + label->setBuddy( m_changedColorButton ); + gridLayout->addWidget( label, 1, 0 ); + gridLayout->addWidget( m_changedColorButton, 1, 1 ); + + // add the addColor + label = new QLabel( i18n( "Added color:" ), colorGroupBox ); + m_addedColorButton = new KColorButton( colorGroupBox ); + label->setBuddy( m_addedColorButton ); + gridLayout->addWidget( label, 2, 0 ); + gridLayout->addWidget( m_addedColorButton, 2, 1 ); + + // add the appliedColor + label = new QLabel( i18n( "Applied color:" ), colorGroupBox ); + m_appliedColorButton = new KColorButton( colorGroupBox ); + label->setBuddy( m_appliedColorButton ); + gridLayout->addWidget( label, 3, 0 ); + gridLayout->addWidget( m_appliedColorButton, 3, 1 ); + + // scroll number of lines (snol) + snolGroupBox = new QGroupBox( page ); + QHBoxLayout *snolLayout = new QHBoxLayout; + snolGroupBox->setLayout( snolLayout ); + snolGroupBox->setTitle( i18n( "Mouse Wheel" ) ); + layout->addWidget( snolGroupBox ); + //snolGroupBox->setMargin( KDialog::marginHint() ); + + label = new QLabel( i18n( "Number of lines:" ) ); + snolLayout->addWidget( label ); + m_snolSpinBox = new QSpinBox( snolGroupBox ); + m_snolSpinBox->setRange( 0, 50 ); + snolLayout->addWidget( m_snolSpinBox ); + label->setBuddy( m_snolSpinBox ); + + // Temporarily here for testing... + // number of spaces for a tab character stuff + tabGroupBox = new QGroupBox( page ); + QHBoxLayout *tabLayout = new QHBoxLayout; + tabGroupBox->setLayout( tabLayout ); + tabGroupBox->setTitle( i18n( "Tabs to Spaces" ) ); + layout->addWidget( tabGroupBox ); + //tabGroupBox->setMargin( KDialog::marginHint() ); + + label = new QLabel( i18n( "Number of spaces to convert a tab character to:" ) ); + tabLayout->addWidget( label ); + m_tabSpinBox = new QSpinBox( tabGroupBox ); + m_tabSpinBox->setRange( 1, 16 ); + tabLayout->addWidget( m_tabSpinBox ); + label->setBuddy( m_tabSpinBox ); + + layout->addStretch( 1 ); + page->setMinimumSize( sizeHintForWidget( page ) ); + + m_tabWidget->addTab( page, i18n( "Appearance" ) ); + + page = new QWidget( m_tabWidget ); + layout = new QVBoxLayout( page ); + layout->setSpacing( KDialog::spacingHint() ); + layout->setMargin( KDialog::marginHint() ); + + QGroupBox* gb = new QGroupBox( page ); + QHBoxLayout *layfont = new QHBoxLayout; + gb->setLayout( layfont ); + gb->setTitle( i18n( "Text Font" ) ); + layout->addWidget( gb ); + //gb->setMargin( KDialog::marginHint() ); + + label = new QLabel( i18n( "Font:" ) ); + m_fontCombo = new QFontComboBox; + layfont->addWidget( label ); + layfont->addWidget( m_fontCombo ); + m_fontCombo->setObjectName( "fontcombo" ); + label->setBuddy( m_fontCombo ); + + label = new QLabel( i18n( "Size:" ) ); + layfont->addWidget( label ); + m_fontSizeSpinBox = new QSpinBox( gb ); + m_fontSizeSpinBox->setRange( 6, 24 ); + layfont->addWidget( m_fontSizeSpinBox ); + label->setBuddy( m_fontSizeSpinBox ); + + layout->addStretch( 1 ); + page->setMinimumSize( sizeHintForWidget( page ) ); + + m_tabWidget->addTab( page, i18n( "Fonts" ) ); +} + +ViewPage::~ViewPage() +{ + +} + +void ViewPage::setSettings( ViewSettings* setts ) +{ + m_settings = setts; + + m_addedColorButton->setColor ( m_settings->m_addColor ); + m_changedColorButton->setColor( m_settings->m_changeColor ); + m_removedColorButton->setColor( m_settings->m_removeColor ); + m_appliedColorButton->setColor( m_settings->m_appliedColor ); + m_snolSpinBox->setValue ( m_settings->m_scrollNoOfLines ); + m_tabSpinBox->setValue ( m_settings->m_tabToNumberOfSpaces ); + + m_fontCombo->setCurrentFont ( m_settings->m_font.family() ); + m_fontSizeSpinBox->setValue ( m_settings->m_font.pointSize() ); +} + +ViewSettings* ViewPage::settings( void ) +{ + return m_settings; +} + +void ViewPage::restore() +{ +} + +void ViewPage::apply() +{ + m_settings->m_addColor = m_addedColorButton->color(); + m_settings->m_changeColor = m_changedColorButton->color(); + m_settings->m_removeColor = m_removedColorButton->color(); + m_settings->m_appliedColor = m_appliedColorButton->color(); + m_settings->m_scrollNoOfLines = m_snolSpinBox->value(); + m_settings->m_tabToNumberOfSpaces = m_tabSpinBox->value(); + + m_settings->m_font = QFont( m_fontCombo->currentFont() ); + m_settings->m_font.setPointSize( m_fontSizeSpinBox->value() ); + + m_settings->saveSettings( KGlobal::config().data() ); +} + +void ViewPage::setDefaults() +{ + m_addedColorButton->setColor ( ViewSettings::default_addColor ); + m_changedColorButton->setColor( ViewSettings::default_changeColor ); + m_removedColorButton->setColor( ViewSettings::default_removeColor ); + m_appliedColorButton->setColor( ViewSettings::default_appliedColor ); + m_snolSpinBox->setValue ( 3 ); + m_tabSpinBox->setValue ( 4 ); + + // TODO: port + // m_fontCombo->setCurrentFont ( KGlobalSettings::fixedFont().family() ); + m_fontSizeSpinBox->setValue ( 10 ); +} + +#include "viewpage.moc" diff --git a/kompare/libdialogpages/viewpage.h b/kompare/libdialogpages/viewpage.h new file mode 100644 index 00000000..b5b770d1 --- /dev/null +++ b/kompare/libdialogpages/viewpage.h @@ -0,0 +1,65 @@ +/*************************************************************************** + generalprefs.h + -------------- + begin : Sun Mar 4 2001 + Copyright 2001-2003 Otto Bruggeman + Copyright 2001-2003 John Firebaugh + Copyright 2007 Kevin Kofler +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 VIEWPAGE_H +#define VIEWPAGE_H + +#include "pagebase.h" +#include "dialogpagesexport.h" + +class QFontComboBox; +class QSpinBox; + +class KColorButton; +class KTabWidget; + +class ViewSettings; + +class DIALOGPAGES_EXPORT ViewPage : public PageBase +{ +Q_OBJECT +public: + ViewPage(); + ~ViewPage(); + +public: + void setSettings( ViewSettings* ); + ViewSettings* settings( void ); + +public: + ViewSettings* m_settings; + +public: + virtual void restore(); + virtual void apply(); + virtual void setDefaults(); + +public: + KColorButton* m_removedColorButton; + KColorButton* m_changedColorButton; + KColorButton* m_addedColorButton; + KColorButton* m_appliedColorButton; + // snol == scroll number of lines + QSpinBox* m_snolSpinBox; + QSpinBox* m_tabSpinBox; + QFontComboBox* m_fontCombo; + QSpinBox* m_fontSizeSpinBox; + KTabWidget* m_tabWidget; +}; + +#endif diff --git a/kompare/libdialogpages/viewsettings.cpp b/kompare/libdialogpages/viewsettings.cpp new file mode 100644 index 00000000..5a69d0bd --- /dev/null +++ b/kompare/libdialogpages/viewsettings.cpp @@ -0,0 +1,101 @@ +/*************************************************************************** + generalsettings.cpp + ------------------- + begin : Sun Mar 4 2001 + Copyright 2001-2003 Otto Bruggeman + Copyright 2001-2003 John Firebaugh + Copyright 2007 Kevin Kofler +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 "viewsettings.h" + +#include + +#include +#include +#include + +using namespace Diff2; + +const QColor ViewSettings::default_removeColor (190, 237, 190); +const QColor ViewSettings::default_changeColor (237, 190, 190); +const QColor ViewSettings::default_addColor (190, 190, 237); +const QColor ViewSettings::default_appliedColor(237, 237, 190); + +ViewSettings::ViewSettings( QWidget* parent ) + : SettingsBase( parent ), + m_removeColor( 0, 0, 0 ), + m_changeColor( 0, 0, 0), + m_addColor( 0, 0, 0), + m_appliedColor( 0, 0, 0), + m_scrollNoOfLines( 0 ), + m_tabToNumberOfSpaces( 0 ) +{ +} + +ViewSettings::~ViewSettings() +{ +} + +void ViewSettings::loadSettings( KConfig* config ) +{ + KConfigGroup cfg( config, "View Options" ); + m_removeColor = cfg.readEntry( "RemoveColor", default_removeColor ); + m_changeColor = cfg.readEntry( "ChangeColor", default_changeColor ); + m_addColor = cfg.readEntry( "AddColor", default_addColor ); + m_appliedColor = cfg.readEntry( "AppliedColor", default_appliedColor ); + m_scrollNoOfLines = cfg.readEntry ( "ScrollNoOfLines", 3 ); + m_tabToNumberOfSpaces = cfg.readEntry ( "TabToNumberOfSpaces", 4 ); + + QFont stdFixed = KGlobalSettings::fixedFont(); + stdFixed.setPointSize( 10 ); + m_font = cfg.readEntry ( "TextFont", stdFixed ); +} + +void ViewSettings::saveSettings( KConfig* config ) +{ + KConfigGroup cfg( config, "View Options" ); + cfg.writeEntry( "RemoveColor", m_removeColor ); + cfg.writeEntry( "ChangeColor", m_changeColor ); + cfg.writeEntry( "AddColor", m_addColor ); + cfg.writeEntry( "AppliedColor", m_appliedColor ); + cfg.writeEntry( "ScrollNoOfLines", m_scrollNoOfLines ); + cfg.writeEntry( "TabToNumberOfSpaces", m_tabToNumberOfSpaces ); + + cfg.writeEntry( "TextFont", m_font ); +} + +QColor ViewSettings::colorForDifferenceType( int type, bool selected, bool applied ) +{ + // FIXME: does not belong here + QColor color; + if( applied ) + color = m_appliedColor; + else + { + type = type & 0xFFFFFFEF; // remove the AppliedByBlend + switch( type ) { + case Difference::Unchanged: color = Qt::white; break; + case Difference::Change: color = m_changeColor; break; + case Difference::Insert: color = m_addColor; break; + case Difference::Delete: color = m_removeColor; break; + default: break; + } + } + + if( selected ) + color = color.light( 110 ); + + return color; +} + +#include "viewsettings.moc" diff --git a/kompare/libdialogpages/viewsettings.h b/kompare/libdialogpages/viewsettings.h new file mode 100644 index 00000000..305e9348 --- /dev/null +++ b/kompare/libdialogpages/viewsettings.h @@ -0,0 +1,61 @@ +/*************************************************************************** + generalsettings.h + ----------------- + begin : Sun Mar 4 2001 + Copyright 2001-2003 Otto Bruggeman + Copyright 2001-2003 John Firebaugh +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 VIEWSETTINGS_H +#define VIEWSETTINGS_H + +#include +#include + +#include +#include + +#include "dialogpagesexport.h" + +class DIALOGPAGES_EXPORT ViewSettings : public SettingsBase +{ +Q_OBJECT +public: + static const QColor default_removeColor; + static const QColor default_changeColor; + static const QColor default_addColor; + static const QColor default_appliedColor; + + ViewSettings( QWidget* parent ); + ~ViewSettings(); +public: + // some virtual functions that will be overloaded from the base class + virtual void loadSettings( KConfig* config ); + virtual void saveSettings( KConfig* config ); + QColor colorForDifferenceType( int type, bool selected = false, bool applied = false ); + +public: + QColor m_removeColor; + QColor m_changeColor; + QColor m_addColor; + QColor m_appliedColor; + QColor m_selectedRemoveColor; + QColor m_selectedChangeColor; + QColor m_selectedAddColor; + QColor m_selectedAppliedColor; + int m_scrollNoOfLines; + int m_tabToNumberOfSpaces; + + QFont m_font; +}; + +#endif // VIEWSETTINGS_H diff --git a/kompare/main.cpp b/kompare/main.cpp new file mode 100644 index 00000000..ac68e242 --- /dev/null +++ b/kompare/main.cpp @@ -0,0 +1,232 @@ +/*************************************************************************** + main.cpp + -------- + begin : Sun Mar 4 2001 + Copyright 2001-2005,2009 Otto Bruggeman + Copyright 2001-2003 John Firebaugh + Copyright 2007-2012 Kevin Kofler +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the GNU General Public License as published by +** the Free Software Foundation; either version 2 of the License, or +** (at your option) any later version. +** +***************************************************************************/ +/** + * @file main.cpp + * This is the main entry point for Kompare. + * The command line arguments are handled and the application is started. + * @author Otto Bruggeman + * @author John Firebaugh + * @author Kevin Kofler + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "kompare_part.h" +#include "kompare_shell.h" +#include "kompareurldialog.h" + +/** + * Program description. + */ +static const char description[] = + I18N_NOOP("A program to view the differences between files and optionally generate a diff" ); + +/** + * Version number. + */ +static const char version[] = "4.1.3"; + +/** + * Setting up the KAboutData structure. + * Parsing and handling of the given command line arguments. + * @param argc the number of arguments + * @param argv the array of arguments + * @return exit status + */ +int main(int argc, char *argv[]) +{ + KAboutData aboutData( "kompare", 0, ki18n("Kompare"), version, ki18n(description), + KAboutData::License_GPL, + ki18n("(c) 2001-2004 John Firebaugh, (c) 2001-2005,2009 Otto Bruggeman, (c) 2004-2005 Jeff Snyder, (c) 2007-2012 Kevin Kofler") ); + aboutData.addAuthor( ki18n("John Firebaugh"), ki18n("Author"), "jfirebaugh@kde.org" ); + aboutData.addAuthor( ki18n("Otto Bruggeman"), ki18n("Author"), "bruggie@gmail.com" ); + aboutData.addAuthor( ki18n("Jeff Snyder"), ki18n("Developer"), "jeff@caffeinated.me.uk" ); + aboutData.addCredit( ki18n("Kevin Kofler"), ki18n("Maintainer"), "kevin.kofler@chello.at" ); + aboutData.addCredit( ki18n("Chris Luetchford"), ki18n("Kompare icon artist"), "chris@os11.com" ); + aboutData.addCredit( ki18n("Malte Starostik"), ki18n("A lot of good advice"), "malte@kde.org" ); + aboutData.addCredit( ki18n("Bernd Gehrmann"), ki18n("Cervisia diff viewer"), "bernd@physik.hu-berlin.de" ); + + KCmdLineArgs::init(argc, argv, &aboutData); + + KCmdLineOptions options; + options.add("c", ki18n( "This will compare URL1 with URL2" )); + options.add("o", ki18n( "This will open URL1 and expect it to be diff output. URL1 can also be a '-' and then it will read from standard input. Can be used for instance for cvs diff | kompare -o -. Kompare will do a check to see if it can find the original file(s) and then blend the original file(s) into the diffoutput and show that in the viewer. -n disables the check." )); + options.add("b", ki18n( "This will blend URL2 into URL1, URL2 is expected to be diff output and URL1 the file or folder that the diffoutput needs to be blended into. " )); + options.add("n", ki18n( "Disables the check for automatically finding the original file(s) when using '-' as URL with the -o option." )); + options.add("e ", ki18n( "Use this to specify the encoding when calling it from the command line. It will default to the local encoding if not specified." )); + options.add("+[URL1 [URL2]]"); + options.add("+-"); + KCmdLineArgs::addCmdLineOptions( options ); + KApplication kompare; + bool difault = false; + + KompareShell* ks; + + // see if we are starting with session management + if (kompare.isSessionRestored()) + { + RESTORE(KompareShell) + } + else + { + KCmdLineArgs *args = KCmdLineArgs::parsedArgs(); + + ks = new KompareShell(); + ks->setObjectName( "FirstKompareShell" ); + + kDebug( 8100 ) << "Arg Count = " << args->count() << endl; + for ( int i=0; i < args->count(); i++ ) + { + kDebug( 8100 ) << "Argument " << (i+1) << ": " << args->arg( i ) << endl; + } + + if ( args->isSet( "e" ) ) + { + // Encoding given... + // FIXME: Need to implement this... + } + + if ( args->isSet( "o" ) ) + { + kDebug( 8100 ) << "Option -o is set" << endl; + if ( args->count() != 1 ) + { + difault = true; + } + else + { + ks->show(); + kDebug( 8100 ) << "OpenDiff..." << endl; + if ( args->arg(0) == QLatin1String("-") ) + ks->openStdin(); + else + ks->openDiff( args->url( 0 ) ); + difault = false; + } + } + else if ( args->isSet( "c" ) ) + { + kDebug( 8100 ) << "Option -c is set" << endl; + if ( args->count() != 2 ) + { + KCmdLineArgs::usage( "kompare" ); + difault = true; + } + else + { + ks->show(); + KUrl url0 = args->url( 0 ); + kDebug( 8100 ) << "URL0 = " << url0.url() << endl; + KUrl url1 = args->url( 1 ); + kDebug( 8100 ) << "URL1 = " << url1.url() << endl; + ks->compare( url0, url1 ); + difault = false; + } + } + else if ( args->isSet( "b" ) ) + { + kDebug( 8100 ) << "Option -b is set" << endl; + if ( args->count() != 2 ) + { + KCmdLineArgs::usage( "kompare" ); + difault = true; + } + else + { + ks->show(); + kDebug( 8100 ) << "blend..." << endl; + KUrl url0 = args->url( 0 ); + kDebug( 8100 ) << "URL0 = " << url0.url() << endl; + KUrl url1 = args->url( 1 ); + kDebug( 8100 ) << "URL1 = " << url1.url() << endl; + ks->blend( url0, url1 ); + difault = false; + } + } + else if ( args->count() == 1 ) + { + ks->show(); + + kDebug( 8100 ) << "Single file. so openDiff/openStdin is only possible..." << endl; + if ( args->arg(0) == QLatin1String("-") ) + ks->openStdin(); + else + ks->openDiff( args->url( 0 ) ); + + difault = false; + } + else if ( args->count() == 2 ) + { + // In this case we are assuming you want to compare files/dirs + // and not blending because that is almost impossible to detect + ks->show(); + kDebug( 8100 ) << "Dunno, we'll have to figure it out later, trying compare for now..." << endl; + KUrl url0 = args->url( 0 ); + kDebug( 8100 ) << "URL0 = " << url0.url() << endl; + KUrl url1 = args->url( 1 ); + kDebug( 8100 ) << "URL1 = " << url1.url() << endl; + ks->compare( url0, url1 ); + difault = false; + } + else if ( args->count() == 0 ) // no options and no args + { + difault = true; + } + + if ( difault ) + { + KompareURLDialog dialog( 0 ); + + dialog.setCaption( i18n("Compare Files or Folders") ); + dialog.setFirstGroupBoxTitle( i18n( "Source" ) ); + dialog.setSecondGroupBoxTitle( i18n( "Destination" ) ); + + KGuiItem compareGuiItem( i18n( "Compare" ), QString(), i18n( "Compare these files or folder" ), i18n( "If you have entered 2 filenames or 2 folders in the fields in this dialog then this button will be enabled and pressing it will start a comparison of the entered files or folders. " ) ); + dialog.setButtonGuiItem( KDialog::Ok, compareGuiItem ); + + dialog.setGroup( "Recent Compare Files" ); + + dialog.setFirstURLRequesterMode( KFile::File|KFile::Directory|KFile::ExistingOnly ); + dialog.setSecondURLRequesterMode( KFile::File|KFile::Directory|KFile::ExistingOnly ); + + if( dialog.exec() == QDialog::Accepted ) + { + ks->show(); + ks->viewPart()->setEncoding( dialog.encoding() ); + ks->compare( dialog.getFirstURL(), dialog.getSecondURL() ); + } + else + { + return -1; + } + } + + args->clear(); + } + + return kompare.exec(); +} + +/* vim: set ts=4 sw=4 noet: */ + diff --git a/kompare/pics/CMakeLists.txt b/kompare/pics/CMakeLists.txt new file mode 100644 index 00000000..512f6e8c --- /dev/null +++ b/kompare/pics/CMakeLists.txt @@ -0,0 +1,5 @@ + +kde4_install_icons( ${ICON_INSTALL_DIR} ) + + + diff --git a/kompare/pics/hi128-app-kompare.png b/kompare/pics/hi128-app-kompare.png new file mode 100644 index 00000000..ffcd33d5 Binary files /dev/null and b/kompare/pics/hi128-app-kompare.png differ diff --git a/kompare/pics/hi16-app-kompare.png b/kompare/pics/hi16-app-kompare.png new file mode 100644 index 00000000..c70d5439 Binary files /dev/null and b/kompare/pics/hi16-app-kompare.png differ diff --git a/kompare/pics/hi22-app-kompare.png b/kompare/pics/hi22-app-kompare.png new file mode 100644 index 00000000..053b1690 Binary files /dev/null and b/kompare/pics/hi22-app-kompare.png differ diff --git a/kompare/pics/hi32-app-kompare.png b/kompare/pics/hi32-app-kompare.png new file mode 100644 index 00000000..1dd3577e Binary files /dev/null and b/kompare/pics/hi32-app-kompare.png differ diff --git a/kompare/pics/hi48-app-kompare.png b/kompare/pics/hi48-app-kompare.png new file mode 100644 index 00000000..8eda48e1 Binary files /dev/null and b/kompare/pics/hi48-app-kompare.png differ diff --git a/kompare/pics/hisc-app-kompare.svgz b/kompare/pics/hisc-app-kompare.svgz new file mode 100644 index 00000000..945a6631 Binary files /dev/null and b/kompare/pics/hisc-app-kompare.svgz differ diff --git a/kompare/tests/cvsdiff/context.diff b/kompare/tests/cvsdiff/context.diff new file mode 100644 index 00000000..c9a7f855 --- /dev/null +++ b/kompare/tests/cvsdiff/context.diff @@ -0,0 +1,83 @@ +Index: client/dcopfind.cpp +=================================================================== +RCS file: /home/kde/kdelibs/dcop/client/dcopfind.cpp,v +retrieving revision 1.2 +diff -c -r1.2 dcopfind.cpp +*** client/dcopfind.cpp 2001/10/31 01:17:39 1.2 +--- client/dcopfind.cpp 2002/01/16 18:07:13 +*************** +*** 36,42 **** + static bool bAppIdOnly = 0; + static bool bLaunchApp = 0; + +! bool findObject( const char* app, const char* obj, const char* func, int argc, char** args ) + { + QString f = func; // Qt is better with unicode strings, so use one. + int left = f.find( '(' ); +--- 36,42 ---- + static bool bAppIdOnly = 0; + static bool bLaunchApp = 0; + +! bool findObject( const char* app, const char* obj, const char* func, QCStringList args ) + { + QString f = func; // Qt is better with unicode strings, so use one. + int left = f.find( '(' ); +*************** +*** 118,124 **** + f = fc; + } + +! if ( (int) types.count() != argc ) { + qWarning( "arguments do not match" ); + exit(1); + } +--- 118,124 ---- + f = fc; + } + +! if ( types.count() != args.count() ) { + qWarning( "arguments do not match" ); + exit(1); + } +*************** +*** 128,136 **** + + int i = 0; + for ( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) { +! marshall(arg, argc, args, i, *it); + } +! if ( (int) i != argc ) { + qWarning( "arguments do not match" ); + exit(1); + } +--- 128,136 ---- + + int i = 0; + for ( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) { +! marshall(arg, args, i, *it); + } +! if ( (uint) i != args.count() ) { + qWarning( "arguments do not match" ); + exit(1); + } +*************** +*** 221,227 **** + argc = 0; + } + +! findObject( app, objid, function, argc, args ); + + return 0; + } +--- 221,231 ---- + argc = 0; + } + +! QCStringList params; +! for( int i = 0; i < argc; i++ ) +! params.append( args[ i ] ); +! +! findObject( app, objid, function, params ); + + return 0; + } diff --git a/kompare/tests/cvsdiff/contextm.diff b/kompare/tests/cvsdiff/contextm.diff new file mode 100644 index 00000000..ef20ec4c --- /dev/null +++ b/kompare/tests/cvsdiff/contextm.diff @@ -0,0 +1,1046 @@ +Index: client/dcop.cpp +=================================================================== +RCS file: /home/kde/kdelibs/dcop/client/dcop.cpp,v +retrieving revision 1.26 +diff -c -r1.26 dcop.cpp +*** client/dcop.cpp 2001/10/31 01:17:39 1.26 +--- client/dcop.cpp 2002/01/16 18:06:24 +*************** +*** 20,38 **** + + ******************************************************************/ + +! #include + #include +! #include "../kdatastream.h" + #include "../dcopclient.h" + #include "../dcopref.h" +! #include +! #include +! #include + + #include "marshall.cpp" + + static DCOPClient* dcop = 0; + + bool startsWith(const QCString &id, const char *str, int n) + { + return !n || (strncmp(id.data(), str, n) == 0); +--- 20,66 ---- + + ******************************************************************/ + +! #include +! #include +! #include +! + #include +! #include +! #include +! #include +! #include +! #include +! #include +! #include +! +! // putenv() is not available on all platforms, so make sure the emulation +! // wrapper is available in those cases by loading config.h! +! #include +! + #include "../dcopclient.h" + #include "../dcopref.h" +! #include "../kdatastream.h" + + #include "marshall.cpp" + ++ typedef QMap UserList; ++ + static DCOPClient* dcop = 0; + ++ static QTextStream cout( stdout, IO_WriteOnly ); ++ static QTextStream cerr( stderr, IO_WriteOnly ); ++ ++ /** ++ * Session to send call to ++ * DefaultSession - current session. Current KDE session when called without ++ * --user or --all-users option. Otherwise this value ignores ++ * all users with more than one active session. ++ * AllSessions - Send to all sessions found. requires --user or --all-users. ++ * QuerySessions - Don't call DCOP, return a list of available sessions. ++ * CustomSession - Use the specified session ++ */ ++ enum Session { DefaultSession = 0, AllSessions, QuerySessions, CustomSession }; ++ + bool startsWith(const QCString &id, const char *str, int n) + { + return !n || (strncmp(id.data(), str, n) == 0); +*************** +*** 118,126 **** + } + } + +! void callFunction( const char* app, const char* obj, const char* func, int argc, char** args ) + { +- + QString f = func; // Qt is better with unicode strings, so use one. + int left = f.find( '(' ); + int right = f.find( ')' ); +--- 146,153 ---- + } + } + +! void callFunction( const char* app, const char* obj, const char* func, const QCStringList args ) + { + QString f = func; // Qt is better with unicode strings, so use one. + int left = f.find( '(' ); + int right = f.find( ')' ); +*************** +*** 136,142 **** + bool ok = false; + QCStringList funcs = dcop->remoteFunctions( app, obj, &ok ); + QCString realfunc; +! if ( !ok && argc == 0 ) + goto doit; + if ( !ok ) + { +--- 163,169 ---- + bool ok = false; + QCStringList funcs = dcop->remoteFunctions( app, obj, &ok ); + QCString realfunc; +! if ( !ok && args.isEmpty() ) + goto doit; + if ( !ok ) + { +*************** +*** 153,167 **** + + if ( l > 0 && (*it).mid( s, l - s ) == func ) { + realfunc = (*it).mid( s ); +! int a = (*it).contains(','); +! if ( ( a == 0 && argc == 0) || ( a > 0 && a + 1 == argc ) ) + break; + } + } + if ( realfunc.isEmpty() ) + { + qWarning("no such function"); +! exit(1); + } + f = realfunc; + left = f.find( '(' ); +--- 180,195 ---- + + if ( l > 0 && (*it).mid( s, l - s ) == func ) { + realfunc = (*it).mid( s ); +! uint a = (*it).contains(','); +! if ( ( a == 0 && args.isEmpty() ) || ( a > 0 && a + 1 == args.count() ) ) + break; + } + } + if ( realfunc.isEmpty() ) + { + qWarning("no such function"); +! // exit(1); +! return; + } + f = realfunc; + left = f.find( '(' ); +*************** +*** 243,253 **** + QCString replyType; + QDataStream arg(data, IO_WriteOnly); + +! int i = 0; +! for ( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) { +! marshall(arg, argc, args, i, *it); +! } +! if ( i != argc ) { + qWarning( "arguments do not match" ); + exit(1); + } +--- 271,282 ---- + QCString replyType; + QDataStream arg(data, IO_WriteOnly); + +! uint i = 0; +! for( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) +! marshall( arg, args, i, *it ); +! +! if ( i != args.count() ) +! { + qWarning( "arguments do not match" ); + exit(1); + } +*************** +*** 265,343 **** + } + } + } +- + + +! int main( int argc, char** argv ) + { + +! if ( argc > 1 && argv[1][0] == '-' ) { +! fprintf( stderr, "Usage: dcop [ application [object [function [arg1] [arg2] [arg3] ... ] ] ] \n" ); +! exit(0); + } + +! DCOPClient client; +! client.attach(); +! dcop = &client; + + QCString app; + QCString objid; + QCString function; +! char **args = 0; +! if ((argc > 1) && (strncmp(argv[1], "DCOPRef(", 8)) == 0) + { +! char *delim = strchr(argv[1], ','); +! if (!delim) +! { +! fprintf(stderr, "Error: '%s' is not a valid DCOP reference.\n", argv[1]); +! return 1; +! } +! *delim = 0; +! app = argv[1] + 8; +! delim++; +! delim[strlen(delim)-1] = 0; +! objid = delim; +! if (argc > 2) +! function = argv[2]; +! if (argc > 3) +! args = &argv[3]; +! argc++; + } + else + { +! if (argc > 1) +! app = argv[1]; +! if (argc > 2) +! objid = argv[2]; +! if (argc > 3) +! function = argv[3]; +! if (argc > 4) +! args = &argv[4]; +! } +! +! switch ( argc ) { +! case 0: +! case 1: +! queryApplications(""); +! break; +! case 2: +! if (endsWith(app, '*')) +! queryApplications(app); +! else +! queryObjects( app, "" ); +! break; +! case 3: +! if (endsWith(objid, '*')) +! queryObjects(app, objid); +! else +! queryFunctions( app, objid ); +! break; +! case 4: +! default: +! callFunction( app, objid, function, argc - 4, args ); +! break; + + } + + return 0; + } +--- 294,773 ---- + } + } + } + ++ /** ++ * Show command-line help and exit ++ */ ++ void showHelp( int exitCode = 0 ) ++ { ++ cout << "Usage: dcop [options] [application [object [function [arg1] [arg2] ... ] ] ]" << endl ++ << "" << endl ++ << "Console DCOP client" << endl ++ << "" << endl ++ << "Generic options:" << endl ++ << " --help Show help about options" << endl ++ << "" << endl ++ << "Options:" << endl ++ << " --pipe Call DCOP for each line read from stdin" << endl ++ << " --user Connect to the given user's DCOP server. This option will" << endl ++ << " ignore the values of the environment vars $DCOPSERVER and" << endl ++ << " $ICEAUTHORITY, even if they are set." << endl ++ << " If the user has more than one open session, you must also" << endl ++ << " use one of the --list-sessions, --session or --als-sessions" << endl ++ << " command-line options." << endl ++ << " --all-users Send the same DCOP call to all users with a running DCOP" << endl ++ << " server. Only failed calls to existing DCOP servers will" ++ << " generate an error message. If no DCOP server is available" << endl ++ << " at all, no error will be generated." << endl; ++ ++ exit( exitCode ); ++ } + +! /** +! * Return a list of all users and their home directories. +! * Returns an empty list if /etc/passwd cannot be read for some reason. +! */ +! static UserList userList() + { ++ UserList result; ++ ++ QFile f( "/etc/passwd" ); ++ ++ if( !f.open( IO_ReadOnly ) ) ++ { ++ cerr << "Can't open /etc/passwd for reading!" << endl; ++ return result; ++ } + +! QStringList l( QStringList::split( '\n', f.readAll() ) ); +! +! for( QStringList::ConstIterator it( l.begin() ); it != l.end(); ++it ) +! { +! QStringList userInfo( QStringList::split( ':', *it, true ) ); +! result[ userInfo[ 0 ] ] = userInfo[ 5 ]; + } + +! return result; +! } +! +! /** +! * Return a list of available DCOP sessions for the specified user +! * An empty list means no sessions are available, or an error occurred. +! */ +! QStringList dcopSessionList( const QString &user, const QString &home ) +! { +! if( home.isEmpty() ) +! { +! cerr << "WARNING: Cannot determine home directory for user " +! << user << "!" << endl +! << "Please check permissions or set the $DCOPSERVER variable manually before" << endl +! << "calling dcop." << endl; +! return QStringList(); +! } +! +! QStringList result; +! QFileInfo dirInfo( home ); +! if( !dirInfo.exists() || !dirInfo.isReadable() ) +! return result; +! +! QDir d( home ); +! d.setFilter( QDir::Files | QDir::Hidden | QDir::NoSymLinks ); +! d.setNameFilter( ".DCOPserver*" ); +! +! const QFileInfoList *list = d.entryInfoList(); +! if( !list ) +! return result; +! +! QFileInfoListIterator it( *list ); +! QFileInfo *fi; +! +! while ( ( fi = it.current() ) != 0 ) +! { +! if( fi->isReadable() ) +! result.append( fi->fileName() ); +! ++it; +! } +! return result; +! } + ++ /** ++ * Do the actual DCOP call ++ */ ++ void runDCOP( QCStringList args, UserList users, Session session, ++ const QString sessionName, bool readStdin ) ++ { + QCString app; + QCString objid; + QCString function; +! QCStringList params; +! DCOPClient *client = 0L; +! if ( !args.isEmpty() && args[ 0 ].find( "DCOPRef(" ) == 0 ) + { +! // WARNING: This part (until the closing '}') could very +! // well be broken now. As I don't know how to trigger and test +! // dcoprefs this code is *not* tested. It compiles and it looks +! // ok to me, but that's all I can say - Martijn (2001/12/24) +! int delimPos = args[ 0 ].findRev( ',' ); +! if( delimPos == -1 ) +! { +! cerr << "Error: '" << args[ 0 ] +! << "' is not a valid DCOP reference." << endl; +! exit( -1 ); +! } +! args[ 0 ][ delimPos ] = 0; +! app = args[ 0 ].mid( 8 ); +! delimPos++; +! args[ 0 ][ args[ 0 ].length() - 1 ] = 0; +! objid = args[ 0 ].mid( delimPos ); +! if( args.count() > 1 ) +! function = args[ 1 ]; +! if( args.count() > 2 ) +! { +! params = args; +! params.remove( params.begin() ); +! params.remove( params.begin() ); +! } + } + else + { +! if( !args.isEmpty() ) +! app = args[ 0 ]; +! if( args.count() > 1 ) +! objid = args[ 1 ]; +! if( args.count() > 2 ) +! function = args[ 2 ]; +! if( args.count() > 3) +! { +! params = args; +! params.remove( params.begin() ); +! params.remove( params.begin() ); +! params.remove( params.begin() ); +! } +! } +! +! bool firstRun = true; +! UserList::Iterator it; +! QStringList sessions; +! bool presetDCOPServer = false; +! // char *dcopStr = 0L; +! QString dcopServer; +! +! for( it = users.begin(); it != users.end() || firstRun; it++ ) +! { +! firstRun = false; +! +! //cout << "Iterating '" << it.key() << "'" << endl; +! +! if( session == QuerySessions ) +! { +! QStringList sessions = dcopSessionList( it.key(), it.data() ); +! if( sessions.isEmpty() ) +! { +! cout << "No active sessions"; +! if( !( *it ).isEmpty() ) +! cout << " for user " << *it; +! cout << endl; +! } +! else +! { +! cout << "Active sessions "; +! if( !( *it ).isEmpty() ) +! cout << "for user " << *it << " "; +! cout << ":" << endl; +! +! QStringList::Iterator sIt; +! for( sIt = sessions.begin(); sIt != sessions.end(); sIt++ ) +! cout << " " << *sIt << endl; +! +! cout << endl; +! } +! continue; +! } +! +! if( getenv( "DCOPSERVER" ) ) +! { +! sessions.append( getenv( "DCOPSERVER" ) ); +! presetDCOPServer = true; +! } +! +! if( users.count() > 1 || ( users.count() == 1 && +! ( getenv( "DCOPSERVER" ) == 0 /*&& getenv( "DISPLAY" ) == 0*/ ) ) ) +! { +! sessions = dcopSessionList( it.key(), it.data() ); +! if( sessions.isEmpty() ) +! { +! if( users.count() > 1 ) +! continue; +! else +! { +! cerr << "ERROR: No active KDE sessions!" << endl +! << "If you are sure there is one, please set the $DCOPSERVER variable manually" << endl +! << "before calling dcop." << endl; +! exit( -1 ); +! } +! } +! else if( sessions.count() > 1 && session != AllSessions ) +! { +! cerr << "ERROR: Multiple available KDE sessions!" << endl +! << "Please specify the correct session to use with --session or use the" << endl +! << "--all-sessions option to broadcast to all sessions." << endl; +! exit( -1 ); +! } +! } + ++ if( users.count() > 1 || ( users.count() == 1 && ++ ( getenv( "ICEAUTHORITY" ) == 0 || getenv( "DISPLAY" ) == 0 ) ) ) ++ { ++ // Check for ICE authority file and if the file can be read by us ++ QString home = it.data(); ++ QString iceFile = it.data() + "/.ICEauthority"; ++ QFileInfo fi( iceFile ); ++ if( iceFile.isEmpty() ) ++ { ++ cerr << "WARNING: Cannot determine home directory for user " ++ << it.key() << "!" << endl ++ << "Please check permissions or set the $ICEAUTHORITY variable manually before" << endl ++ << "calling dcop." << endl; ++ } ++ else if( fi.exists() ) ++ { ++ if( fi.isReadable() ) ++ { ++ char *envStr = strdup( ( "ICEAUTHORITY=" + iceFile ).ascii() ); ++ putenv( envStr ); ++ //cerr << "ice: " << envStr << endl; ++ } ++ else ++ { ++ cerr << "WARNING: ICE authority file " << iceFile ++ << "is not readable by you!" << endl ++ << "Please check permissions or set the $ICEAUTHORITY variable manually before" << endl ++ << "calling dcop." << endl; ++ } ++ } ++ else ++ { ++ if( users.count() > 1 ) ++ continue; ++ else ++ { ++ cerr << "WARNING: Cannot find ICE authority file " ++ << iceFile << "!" << endl ++ << "Please check permissions or set the $ICEAUTHORITY" ++ << " variable manually before" << endl ++ << "calling dcop." << endl; ++ } ++ } ++ } ++ ++ // Main loop ++ // If users is an empty list we're calling for the currently logged ++ // in user. In this case we don't have a session, but still want ++ // to iterate the loop once. ++ QStringList::Iterator sIt = sessions.begin(); ++ for( ; sIt != sessions.end() || users.isEmpty(); sIt++ ) ++ { ++ if( !presetDCOPServer && !users.isEmpty() ) ++ { ++ QString dcopFile = it.data() + "/" + *sIt; ++ QFile f( dcopFile ); ++ if( !f.open( IO_ReadOnly ) ) ++ { ++ cerr << "Can't open " << dcopFile << " for reading!" << endl; ++ exit( -1 ); ++ } ++ ++ QStringList l( QStringList::split( '\n', f.readAll() ) ); ++ dcopServer = l.first(); ++ ++ if( dcopServer.isEmpty() ) ++ { ++ cerr << "WARNING: Unable to determine DCOP server for session " ++ << *sIt << "!" << endl ++ << "Please check permissions or set the $DCOPSERVER variable manually before" << endl ++ << "calling dcop." << endl; ++ exit( -1 ); ++ } ++ } ++ ++ delete client; ++ client = new DCOPClient; ++ if( !dcopServer.isEmpty() ) ++ client->setServerAddress( dcopServer.ascii() ); ++ bool success = client->attach(); ++ if( !success ) ++ { ++ cerr << "ERROR: Couldn't attach to DCOP server!" << endl; ++ continue; ++ } ++ dcop = client; ++ ++ switch ( args.count() ) ++ { ++ case 0: ++ queryApplications(""); ++ break; ++ case 1: ++ if (endsWith(app, '*')) ++ queryApplications(app); ++ else ++ queryObjects( app, "" ); ++ break; ++ case 2: ++ if (endsWith(objid, '*')) ++ queryObjects(app, objid); ++ else ++ queryFunctions( app, objid ); ++ break; ++ case 3: ++ default: ++ if( readStdin ) ++ { ++ QCStringList::Iterator replaceArg = args.end(); ++ ++ QCStringList::Iterator it; ++ for( it = args.begin(); it != args.end(); it++ ) ++ if( *it == "%1" ) ++ replaceArg = it; ++ ++ // Read from stdin until EOF and call function for each line read ++ char *buf = new char[ 1000 ]; ++ while ( !feof( stdin ) ) ++ { ++ fgets( buf, 1000, stdin ); ++ ++ if( replaceArg != args.end() ) ++ *replaceArg = buf; ++ ++ callFunction( app, objid, function, params ); ++ } ++ } ++ else ++ { ++ // Just call function ++ // cout << "call " << app << ", " << objid << ", " << function << ", (params)" << endl; ++ callFunction( app, objid, function, params ); ++ } ++ break; ++ } ++ // Another sIt++ would make the loop infinite... ++ if( users.isEmpty() ) ++ break; ++ } ++ ++ // Another it++ would make the loop infinite... ++ if( it == users.end() ) ++ break; + } ++ } ++ + ++ int main( int argc, char** argv ) ++ { ++ bool readStdin = false; ++ int numOptions = 0; ++ QString user; ++ Session session = DefaultSession; ++ QString sessionName; ++ ++ // Scan for command-line options first ++ for( int pos = 1 ; pos <= argc - 1 ; pos++ ) ++ { ++ if( strcmp( argv[ pos ], "--help" ) == 0 ) ++ showHelp( 0 ); ++ else if( strcmp( argv[ pos ], "--pipe" ) == 0 ) ++ { ++ readStdin = true; ++ numOptions++; ++ } ++ else if( strcmp( argv[ pos ], "--user" ) == 0 ) ++ { ++ if( pos <= argc - 2 ) ++ { ++ user = QString::fromLocal8Bit( argv[ pos + 1] ); ++ numOptions +=2; ++ pos++; ++ } ++ else ++ { ++ cerr << "Missing username for '--user' option!" << endl << endl; ++ showHelp( -1 ); ++ } ++ } ++ else if( strcmp( argv[ pos ], "--all-users" ) == 0 ) ++ { ++ user = "*"; ++ numOptions ++; ++ } ++ else if( strcmp( argv[ pos ], "--list-sessions" ) == 0 ) ++ { ++ session = QuerySessions; ++ numOptions ++; ++ } ++ else if( strcmp( argv[ pos ], "--all-sessions" ) == 0 ) ++ { ++ session = AllSessions; ++ numOptions ++; ++ } ++ else if( argv[ pos ][ 0 ] == '-' ) ++ { ++ cerr << "Unknown command-line option '" << argv[ pos ] ++ << "'." << endl << endl; ++ showHelp( -1 ); ++ } ++ else ++ break; // End of options ++ } ++ ++ argc -= numOptions; ++ ++ QCStringList args; ++ for( int i = numOptions; i < argc + numOptions - 1; i++ ) ++ args.append( argv[ i + 1 ] ); ++ ++ if( readStdin && args.count() < 3 ) ++ { ++ cerr << "--pipe option only supported for function calls!" << endl << endl; ++ showHelp( -1 ); ++ } ++ ++ if( user == "*" && args.count() < 3 && session != QuerySessions ) ++ { ++ cerr << "ERROR: The --all-users option is only supported for function calls!" << endl << endl; ++ showHelp( -1 ); ++ } ++ ++ if( session == QuerySessions && !args.isEmpty() ) ++ { ++ cerr << "ERROR: The --list-sessions option cannot be used for actual DCOP calls!" << endl << endl; ++ showHelp( -1 ); ++ } ++ ++ if( session == QuerySessions && user.isEmpty() ) ++ { ++ cerr << "ERROR: The --list-sessions option can only be used with the --user or" << endl ++ << "--all-users options!" << endl << endl; ++ showHelp( -1 ); ++ } ++ ++ if( session != DefaultSession && session != QuerySessions && ++ args.count() < 3 ) ++ { ++ cerr << "ERROR: The --session and --all-sessions options are only supported for function" << endl ++ << "calls!" << endl << endl; ++ showHelp( -1 ); ++ } ++ ++ UserList users; ++ if( user == "*" ) ++ users = userList(); ++ else if( !user.isEmpty() ) ++ users[ user ] = userList()[ user ]; ++ ++ runDCOP( args, users, session, sessionName, readStdin ); ++ + return 0; + } ++ ++ // vim: set ts=8 sts=4 sw=4 noet: ++ +Index: client/dcopfind.cpp +=================================================================== +RCS file: /home/kde/kdelibs/dcop/client/dcopfind.cpp,v +retrieving revision 1.2 +diff -c -r1.2 dcopfind.cpp +*** client/dcopfind.cpp 2001/10/31 01:17:39 1.2 +--- client/dcopfind.cpp 2002/01/16 18:06:24 +*************** +*** 36,42 **** + static bool bAppIdOnly = 0; + static bool bLaunchApp = 0; + +! bool findObject( const char* app, const char* obj, const char* func, int argc, char** args ) + { + QString f = func; // Qt is better with unicode strings, so use one. + int left = f.find( '(' ); +--- 36,42 ---- + static bool bAppIdOnly = 0; + static bool bLaunchApp = 0; + +! bool findObject( const char* app, const char* obj, const char* func, QCStringList args ) + { + QString f = func; // Qt is better with unicode strings, so use one. + int left = f.find( '(' ); +*************** +*** 118,124 **** + f = fc; + } + +! if ( (int) types.count() != argc ) { + qWarning( "arguments do not match" ); + exit(1); + } +--- 118,124 ---- + f = fc; + } + +! if ( types.count() != args.count() ) { + qWarning( "arguments do not match" ); + exit(1); + } +*************** +*** 128,136 **** + + int i = 0; + for ( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) { +! marshall(arg, argc, args, i, *it); + } +! if ( (int) i != argc ) { + qWarning( "arguments do not match" ); + exit(1); + } +--- 128,136 ---- + + int i = 0; + for ( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) { +! marshall(arg, args, i, *it); + } +! if ( (uint) i != args.count() ) { + qWarning( "arguments do not match" ); + exit(1); + } +*************** +*** 221,227 **** + argc = 0; + } + +! findObject( app, objid, function, argc, args ); + + return 0; + } +--- 221,231 ---- + argc = 0; + } + +! QCStringList params; +! for( int i = 0; i < argc; i++ ) +! params.append( args[ i ] ); +! +! findObject( app, objid, function, params ); + + return 0; + } +Index: client/marshall.cpp +=================================================================== +RCS file: /home/kde/kdelibs/dcop/client/marshall.cpp,v +retrieving revision 1.3 +diff -c -r1.3 marshall.cpp +*** client/marshall.cpp 2001/10/31 01:17:39 1.3 +--- client/marshall.cpp 2002/01/16 18:06:24 +*************** +*** 242,349 **** + + } + +! void marshall(QDataStream &arg, int argc, char **argv, int &i, QString type) + { +! if (type == "QStringList") +! type = "QValueList"; +! if (type == "QCStringList") +! type = "QValueList"; +! if (i >= argc) +! { +! qWarning("Not enough arguments."); +! exit(1); +! } +! QString s = QString::fromLocal8Bit(argv[i]); +! +! if ( type == "int" ) +! arg << s.toInt(); +! else if ( type == "uint" ) +! arg << s.toUInt(); +! else if ( type == "unsigned" ) +! arg << s.toUInt(); +! else if ( type == "unsigned int" ) +! arg << s.toUInt(); +! else if ( type == "long" ) +! arg << s.toLong(); +! else if ( type == "long int" ) +! arg << s.toLong(); +! else if ( type == "unsigned long" ) +! arg << s.toULong(); +! else if ( type == "unsigned long int" ) +! arg << s.toULong(); +! else if ( type == "float" ) +! arg << s.toFloat(); +! else if ( type == "double" ) +! arg << s.toDouble(); +! else if ( type == "bool" ) +! arg << mkBool( s ); +! else if ( type == "QString" ) +! arg << s; +! else if ( type == "QCString" ) +! arg << QCString( argv[i] ); +! else if ( type == "QColor" ) +! arg << mkColor( s ); +! else if ( type == "QPoint" ) +! arg << mkPoint( s ); +! else if ( type == "QSize" ) +! arg << mkSize( s ); +! else if ( type == "QRect" ) +! arg << mkRect( s ); +! else if ( type == "QVariant" ) { +! if ( s == "true" || s == "false" ) +! arg << QVariant( mkBool( s ), 42 ); +! else if ( s.left( 4 ) == "int(" ) +! arg << QVariant( s.mid(4, s.length()-5).toInt() ); +! else if ( s.left( 7 ) == "QPoint(" ) +! arg << QVariant( mkPoint( s.mid(7, s.length()-8) ) ); +! else if ( s.left( 6 ) == "QSize(" ) +! arg << QVariant( mkSize( s.mid(6, s.length()-7) ) ); +! else if ( s.left( 6 ) == "QRect(" ) +! arg << QVariant( mkRect( s.mid(6, s.length()-7) ) ); +! else if ( s.left( 7 ) == "QColor(" ) +! arg << QVariant( mkColor( s.mid(7, s.length()-8) ) ); +! else +! arg << QVariant( s ); +! } else if ( type.startsWith("QValueList<")) { +! type = type.mid(11, type.length() - 12); +! QStringList list; +! QString delim = s; +! if (delim == "[") +! delim = "]"; +! if (delim == "(") +! delim = ")"; +! i++; +! QByteArray dummy_data; +! QDataStream dummy_arg(dummy_data, IO_WriteOnly); + +! int j = i; +! int count = 0; +! // Parse list to get the count +! while (true) { +! if (j >= argc) +! { +! qWarning("List end-delimiter '%s' not found.", delim.latin1()); +! exit(1); +! } +! if (argv[j] == delim) break; +! marshall(dummy_arg, argc, argv, j, type); +! count++; +! } +! arg << (Q_UINT32) count; +! // Parse the list for real +! while (true) { +! if (i >= argc) +! { +! qWarning("List end-delimiter '%s' not found.", delim.latin1()); +! exit(1); +! } +! if (argv[i] == delim) break; +! marshall(arg, argc, argv, i, type); +! } +! } else { +! qWarning( "cannot handle datatype '%s'", type.latin1() ); +! exit(1); +! } + i++; + } + +--- 242,351 ---- + + } + +! void marshall( QDataStream &arg, QCStringList args, uint &i, QString type ) + { +! if (type == "QStringList") +! type = "QValueList"; +! if (type == "QCStringList") +! type = "QValueList"; +! if( i > args.count() ) +! { +! qWarning("Not enough arguments."); +! exit(1); +! } +! QString s = QString::fromLocal8Bit( args[ i ] ); + +! if ( type == "int" ) +! arg << s.toInt(); +! else if ( type == "uint" ) +! arg << s.toUInt(); +! else if ( type == "unsigned" ) +! arg << s.toUInt(); +! else if ( type == "unsigned int" ) +! arg << s.toUInt(); +! else if ( type == "long" ) +! arg << s.toLong(); +! else if ( type == "long int" ) +! arg << s.toLong(); +! else if ( type == "unsigned long" ) +! arg << s.toULong(); +! else if ( type == "unsigned long int" ) +! arg << s.toULong(); +! else if ( type == "float" ) +! arg << s.toFloat(); +! else if ( type == "double" ) +! arg << s.toDouble(); +! else if ( type == "bool" ) +! arg << mkBool( s ); +! else if ( type == "QString" ) +! arg << s; +! else if ( type == "QCString" ) +! arg << QCString( args[ i ] ); +! else if ( type == "QColor" ) +! arg << mkColor( s ); +! else if ( type == "QPoint" ) +! arg << mkPoint( s ); +! else if ( type == "QSize" ) +! arg << mkSize( s ); +! else if ( type == "QRect" ) +! arg << mkRect( s ); +! else if ( type == "QVariant" ) { +! if ( s == "true" || s == "false" ) +! arg << QVariant( mkBool( s ), 42 ); +! else if ( s.left( 4 ) == "int(" ) +! arg << QVariant( s.mid(4, s.length()-5).toInt() ); +! else if ( s.left( 7 ) == "QPoint(" ) +! arg << QVariant( mkPoint( s.mid(7, s.length()-8) ) ); +! else if ( s.left( 6 ) == "QSize(" ) +! arg << QVariant( mkSize( s.mid(6, s.length()-7) ) ); +! else if ( s.left( 6 ) == "QRect(" ) +! arg << QVariant( mkRect( s.mid(6, s.length()-7) ) ); +! else if ( s.left( 7 ) == "QColor(" ) +! arg << QVariant( mkColor( s.mid(7, s.length()-8) ) ); +! else +! arg << QVariant( s ); +! } else if ( type.startsWith("QValueList<")) { +! type = type.mid(11, type.length() - 12); +! QStringList list; +! QString delim = s; +! if (delim == "[") +! delim = "]"; +! if (delim == "(") +! delim = ")"; + i++; ++ QByteArray dummy_data; ++ QDataStream dummy_arg(dummy_data, IO_WriteOnly); ++ ++ uint j = i; ++ uint count = 0; ++ // Parse list to get the count ++ while (true) { ++ if( j > args.count() ) ++ { ++ qWarning("List end-delimiter '%s' not found.", delim.latin1()); ++ exit(1); ++ } ++ if( QString::fromLocal8Bit( args[ j ] ) == delim ) ++ break; ++ marshall( dummy_arg, args, j, type ); ++ count++; ++ } ++ arg << (Q_UINT32) count; ++ // Parse the list for real ++ while (true) { ++ if( i > args.count() ) ++ { ++ qWarning("List end-delimiter '%s' not found.", delim.latin1()); ++ exit(1); ++ } ++ if( QString::fromLocal8Bit( args[ i ] ) == delim ) ++ break; ++ marshall( arg, args, i, type ); ++ } ++ } else { ++ qWarning( "cannot handle datatype '%s'", type.latin1() ); ++ exit(1); ++ } ++ i++; + } + diff --git a/kompare/tests/cvsdiff/ed.diff b/kompare/tests/cvsdiff/ed.diff new file mode 100644 index 00000000..2c859e61 --- /dev/null +++ b/kompare/tests/cvsdiff/ed.diff @@ -0,0 +1,24 @@ +Index: client/dcopfind.cpp +=================================================================== +RCS file: /home/kde/kdelibs/dcop/client/dcopfind.cpp,v +retrieving revision 1.2 +diff -e -r1.2 dcopfind.cpp +224c + QCStringList params; + for( int i = 0; i < argc; i++ ) + params.append( args[ i ] ); + + findObject( app, objid, function, params ); +. +133c + if ( (uint) i != args.count() ) { +. +131c + marshall(arg, args, i, *it); +. +121c + if ( types.count() != args.count() ) { +. +39c +bool findObject( const char* app, const char* obj, const char* func, QCStringList args ) +. diff --git a/kompare/tests/cvsdiff/edm.diff b/kompare/tests/cvsdiff/edm.diff new file mode 100644 index 00000000..0fb04575 --- /dev/null +++ b/kompare/tests/cvsdiff/edm.diff @@ -0,0 +1,692 @@ +Index: client/dcop.cpp +=================================================================== +RCS file: /home/kde/kdelibs/dcop/client/dcop.cpp,v +retrieving revision 1.26 +diff -e -r1.26 dcop.cpp +343a + +// vim: set ts=8 sts=4 sw=4 noet: + +. +340a +} + + +int main( int argc, char** argv ) +{ + bool readStdin = false; + int numOptions = 0; + QString user; + Session session = DefaultSession; + QString sessionName; + + // Scan for command-line options first + for( int pos = 1 ; pos <= argc - 1 ; pos++ ) + { + if( strcmp( argv[ pos ], "--help" ) == 0 ) + showHelp( 0 ); + else if( strcmp( argv[ pos ], "--pipe" ) == 0 ) + { + readStdin = true; + numOptions++; + } + else if( strcmp( argv[ pos ], "--user" ) == 0 ) + { + if( pos <= argc - 2 ) + { + user = QString::fromLocal8Bit( argv[ pos + 1] ); + numOptions +=2; + pos++; + } + else + { + cerr << "Missing username for '--user' option!" << endl << endl; + showHelp( -1 ); + } + } + else if( strcmp( argv[ pos ], "--all-users" ) == 0 ) + { + user = "*"; + numOptions ++; + } + else if( strcmp( argv[ pos ], "--list-sessions" ) == 0 ) + { + session = QuerySessions; + numOptions ++; + } + else if( strcmp( argv[ pos ], "--all-sessions" ) == 0 ) + { + session = AllSessions; + numOptions ++; + } + else if( argv[ pos ][ 0 ] == '-' ) + { + cerr << "Unknown command-line option '" << argv[ pos ] + << "'." << endl << endl; + showHelp( -1 ); + } + else + break; // End of options + } + + argc -= numOptions; + + QCStringList args; + for( int i = numOptions; i < argc + numOptions - 1; i++ ) + args.append( argv[ i + 1 ] ); + + if( readStdin && args.count() < 3 ) + { + cerr << "--pipe option only supported for function calls!" << endl << endl; + showHelp( -1 ); + } + + if( user == "*" && args.count() < 3 && session != QuerySessions ) + { + cerr << "ERROR: The --all-users option is only supported for function calls!" << endl << endl; + showHelp( -1 ); + } + + if( session == QuerySessions && !args.isEmpty() ) + { + cerr << "ERROR: The --list-sessions option cannot be used for actual DCOP calls!" << endl << endl; + showHelp( -1 ); + } + + if( session == QuerySessions && user.isEmpty() ) + { + cerr << "ERROR: The --list-sessions option can only be used with the --user or" << endl + << "--all-users options!" << endl << endl; + showHelp( -1 ); + } + + if( session != DefaultSession && session != QuerySessions && + args.count() < 3 ) + { + cerr << "ERROR: The --session and --all-sessions options are only supported for function" << endl + << "calls!" << endl << endl; + showHelp( -1 ); + } + + UserList users; + if( user == "*" ) + users = userList(); + else if( !user.isEmpty() ) + users[ user ] = userList()[ user ]; + + runDCOP( args, users, session, sessionName, readStdin ); +. +339a + if( users.count() > 1 || ( users.count() == 1 && + ( getenv( "ICEAUTHORITY" ) == 0 || getenv( "DISPLAY" ) == 0 ) ) ) + { + // Check for ICE authority file and if the file can be read by us + QString home = it.data(); + QString iceFile = it.data() + "/.ICEauthority"; + QFileInfo fi( iceFile ); + if( iceFile.isEmpty() ) + { + cerr << "WARNING: Cannot determine home directory for user " + << it.key() << "!" << endl + << "Please check permissions or set the $ICEAUTHORITY variable manually before" << endl + << "calling dcop." << endl; + } + else if( fi.exists() ) + { + if( fi.isReadable() ) + { + char *envStr = strdup( ( "ICEAUTHORITY=" + iceFile ).ascii() ); + putenv( envStr ); + //cerr << "ice: " << envStr << endl; + } + else + { + cerr << "WARNING: ICE authority file " << iceFile + << "is not readable by you!" << endl + << "Please check permissions or set the $ICEAUTHORITY variable manually before" << endl + << "calling dcop." << endl; + } + } + else + { + if( users.count() > 1 ) + continue; + else + { + cerr << "WARNING: Cannot find ICE authority file " + << iceFile << "!" << endl + << "Please check permissions or set the $ICEAUTHORITY" + << " variable manually before" << endl + << "calling dcop." << endl; + } + } + } + + // Main loop + // If users is an empty list we're calling for the currently logged + // in user. In this case we don't have a session, but still want + // to iterate the loop once. + QStringList::Iterator sIt = sessions.begin(); + for( ; sIt != sessions.end() || users.isEmpty(); sIt++ ) + { + if( !presetDCOPServer && !users.isEmpty() ) + { + QString dcopFile = it.data() + "/" + *sIt; + QFile f( dcopFile ); + if( !f.open( IO_ReadOnly ) ) + { + cerr << "Can't open " << dcopFile << " for reading!" << endl; + exit( -1 ); + } + + QStringList l( QStringList::split( '\n', f.readAll() ) ); + dcopServer = l.first(); + + if( dcopServer.isEmpty() ) + { + cerr << "WARNING: Unable to determine DCOP server for session " + << *sIt << "!" << endl + << "Please check permissions or set the $DCOPSERVER variable manually before" << endl + << "calling dcop." << endl; + exit( -1 ); + } + } + + delete client; + client = new DCOPClient; + if( !dcopServer.isEmpty() ) + client->setServerAddress( dcopServer.ascii() ); + bool success = client->attach(); + if( !success ) + { + cerr << "ERROR: Couldn't attach to DCOP server!" << endl; + continue; + } + dcop = client; + + switch ( args.count() ) + { + case 0: + queryApplications(""); + break; + case 1: + if (endsWith(app, '*')) + queryApplications(app); + else + queryObjects( app, "" ); + break; + case 2: + if (endsWith(objid, '*')) + queryObjects(app, objid); + else + queryFunctions( app, objid ); + break; + case 3: + default: + if( readStdin ) + { + QCStringList::Iterator replaceArg = args.end(); + + QCStringList::Iterator it; + for( it = args.begin(); it != args.end(); it++ ) + if( *it == "%1" ) + replaceArg = it; + + // Read from stdin until EOF and call function for each line read + char *buf = new char[ 1000 ]; + while ( !feof( stdin ) ) + { + fgets( buf, 1000, stdin ); + + if( replaceArg != args.end() ) + *replaceArg = buf; + + callFunction( app, objid, function, params ); + } + } + else + { + // Just call function +// cout << "call " << app << ", " << objid << ", " << function << ", (params)" << endl; + callFunction( app, objid, function, params ); + } + break; + } + // Another sIt++ would make the loop infinite... + if( users.isEmpty() ) + break; + } + + // Another it++ would make the loop infinite... + if( it == users.end() ) + break; +. +308,338c + if( !args.isEmpty() ) + app = args[ 0 ]; + if( args.count() > 1 ) + objid = args[ 1 ]; + if( args.count() > 2 ) + function = args[ 2 ]; + if( args.count() > 3) + { + params = args; + params.remove( params.begin() ); + params.remove( params.begin() ); + params.remove( params.begin() ); + } + } + + bool firstRun = true; + UserList::Iterator it; + QStringList sessions; + bool presetDCOPServer = false; +// char *dcopStr = 0L; + QString dcopServer; + + for( it = users.begin(); it != users.end() || firstRun; it++ ) + { + firstRun = false; + + //cout << "Iterating '" << it.key() << "'" << endl; + + if( session == QuerySessions ) + { + QStringList sessions = dcopSessionList( it.key(), it.data() ); + if( sessions.isEmpty() ) + { + cout << "No active sessions"; + if( !( *it ).isEmpty() ) + cout << " for user " << *it; + cout << endl; + } + else + { + cout << "Active sessions "; + if( !( *it ).isEmpty() ) + cout << "for user " << *it << " "; + cout << ":" << endl; + + QStringList::Iterator sIt; + for( sIt = sessions.begin(); sIt != sessions.end(); sIt++ ) + cout << " " << *sIt << endl; + + cout << endl; + } + continue; + } + + if( getenv( "DCOPSERVER" ) ) + { + sessions.append( getenv( "DCOPSERVER" ) ); + presetDCOPServer = true; + } + + if( users.count() > 1 || ( users.count() == 1 && + ( getenv( "DCOPSERVER" ) == 0 /*&& getenv( "DISPLAY" ) == 0*/ ) ) ) + { + sessions = dcopSessionList( it.key(), it.data() ); + if( sessions.isEmpty() ) + { + if( users.count() > 1 ) + continue; + else + { + cerr << "ERROR: No active KDE sessions!" << endl + << "If you are sure there is one, please set the $DCOPSERVER variable manually" << endl + << "before calling dcop." << endl; + exit( -1 ); + } + } + else if( sessions.count() > 1 && session != AllSessions ) + { + cerr << "ERROR: Multiple available KDE sessions!" << endl + << "Please specify the correct session to use with --session or use the" << endl + << "--all-sessions option to broadcast to all sessions." << endl; + exit( -1 ); + } + } +. +289,304c + // WARNING: This part (until the closing '}') could very + // well be broken now. As I don't know how to trigger and test + // dcoprefs this code is *not* tested. It compiles and it looks + // ok to me, but that's all I can say - Martijn (2001/12/24) + int delimPos = args[ 0 ].findRev( ',' ); + if( delimPos == -1 ) + { + cerr << "Error: '" << args[ 0 ] + << "' is not a valid DCOP reference." << endl; + exit( -1 ); + } + args[ 0 ][ delimPos ] = 0; + app = args[ 0 ].mid( 8 ); + delimPos++; + args[ 0 ][ args[ 0 ].length() - 1 ] = 0; + objid = args[ 0 ].mid( delimPos ); + if( args.count() > 1 ) + function = args[ 1 ]; + if( args.count() > 2 ) + { + params = args; + params.remove( params.begin() ); + params.remove( params.begin() ); + } +. +286,287c + QCStringList params; + DCOPClient *client = 0L; + if ( !args.isEmpty() && args[ 0 ].find( "DCOPRef(" ) == 0 ) +. +282a +/** + * Do the actual DCOP call + */ +void runDCOP( QCStringList args, UserList users, Session session, + const QString sessionName, bool readStdin ) +{ +. +279,281c + return result; +} + +/** + * Return a list of available DCOP sessions for the specified user + * An empty list means no sessions are available, or an error occurred. + */ +QStringList dcopSessionList( const QString &user, const QString &home ) +{ + if( home.isEmpty() ) + { + cerr << "WARNING: Cannot determine home directory for user " + << user << "!" << endl + << "Please check permissions or set the $DCOPSERVER variable manually before" << endl + << "calling dcop." << endl; + return QStringList(); + } + + QStringList result; + QFileInfo dirInfo( home ); + if( !dirInfo.exists() || !dirInfo.isReadable() ) + return result; + + QDir d( home ); + d.setFilter( QDir::Files | QDir::Hidden | QDir::NoSymLinks ); + d.setNameFilter( ".DCOPserver*" ); + + const QFileInfoList *list = d.entryInfoList(); + if( !list ) + return result; + + QFileInfoListIterator it( *list ); + QFileInfo *fi; + + while ( ( fi = it.current() ) != 0 ) + { + if( fi->isReadable() ) + result.append( fi->fileName() ); + ++it; + } + return result; +} +. +274,276c + QStringList l( QStringList::split( '\n', f.readAll() ) ); + + for( QStringList::ConstIterator it( l.begin() ); it != l.end(); ++it ) + { + QStringList userInfo( QStringList::split( ':', *it, true ) ); + result[ userInfo[ 0 ] ] = userInfo[ 5 ]; +. +272a + UserList result; + + QFile f( "/etc/passwd" ); + + if( !f.open( IO_ReadOnly ) ) + { + cerr << "Can't open /etc/passwd for reading!" << endl; + return result; + } +. +270,271c +/** + * Return a list of all users and their home directories. + * Returns an empty list if /etc/passwd cannot be read for some reason. + */ +static UserList userList() +. +268a +/** + * Show command-line help and exit + */ +void showHelp( int exitCode = 0 ) +{ + cout << "Usage: dcop [options] [application [object [function [arg1] [arg2] ... ] ] ]" << endl + << "" << endl + << "Console DCOP client" << endl + << "" << endl + << "Generic options:" << endl + << " --help Show help about options" << endl + << "" << endl + << "Options:" << endl + << " --pipe Call DCOP for each line read from stdin" << endl + << " --user Connect to the given user's DCOP server. This option will" << endl + << " ignore the values of the environment vars $DCOPSERVER and" << endl + << " $ICEAUTHORITY, even if they are set." << endl + << " If the user has more than one open session, you must also" << endl + << " use one of the --list-sessions, --session or --als-sessions" << endl + << " command-line options." << endl + << " --all-users Send the same DCOP call to all users with a running DCOP" << endl + << " server. Only failed calls to existing DCOP servers will" + << " generate an error message. If no DCOP server is available" << endl + << " at all, no error will be generated." << endl; + + exit( exitCode ); +} +. +246,250c + uint i = 0; + for( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) + marshall( arg, args, i, *it ); + + if ( i != args.count() ) + { +. +164c +// exit(1); + return; +. +156,157c + uint a = (*it).contains(','); + if ( ( a == 0 && args.isEmpty() ) || ( a > 0 && a + 1 == args.count() ) ) +. +139c + if ( !ok && args.isEmpty() ) +. +123d +121c +void callFunction( const char* app, const char* obj, const char* func, const QCStringList args ) +. +35a +static QTextStream cout( stdout, IO_WriteOnly ); +static QTextStream cerr( stderr, IO_WriteOnly ); + +/** + * Session to send call to + * DefaultSession - current session. Current KDE session when called without + * --user or --all-users option. Otherwise this value ignores + * all users with more than one active session. + * AllSessions - Send to all sessions found. requires --user or --all-users. + * QuerySessions - Don't call DCOP, return a list of available sessions. + * CustomSession - Use the specified session + */ +enum Session { DefaultSession = 0, AllSessions, QuerySessions, CustomSession }; + +. +33a +typedef QMap UserList; + +. +28,30c +#include "../kdatastream.h" +. +25c +#include +#include +#include +#include +#include +#include +#include + +// putenv() is not available on all platforms, so make sure the emulation +// wrapper is available in those cases by loading config.h! +#include + +. +23c +#include +#include +#include + +. +Index: client/dcopfind.cpp +=================================================================== +RCS file: /home/kde/kdelibs/dcop/client/dcopfind.cpp,v +retrieving revision 1.2 +diff -e -r1.2 dcopfind.cpp +224c + QCStringList params; + for( int i = 0; i < argc; i++ ) + params.append( args[ i ] ); + + findObject( app, objid, function, params ); +. +133c + if ( (uint) i != args.count() ) { +. +131c + marshall(arg, args, i, *it); +. +121c + if ( types.count() != args.count() ) { +. +39c +bool findObject( const char* app, const char* obj, const char* func, QCStringList args ) +. +Index: client/marshall.cpp +=================================================================== +RCS file: /home/kde/kdelibs/dcop/client/marshall.cpp,v +retrieving revision 1.3 +diff -e -r1.3 marshall.cpp +347a + QByteArray dummy_data; + QDataStream dummy_arg(dummy_data, IO_WriteOnly); + + uint j = i; + uint count = 0; + // Parse list to get the count + while (true) { + if( j > args.count() ) + { + qWarning("List end-delimiter '%s' not found.", delim.latin1()); + exit(1); + } + if( QString::fromLocal8Bit( args[ j ] ) == delim ) + break; + marshall( dummy_arg, args, j, type ); + count++; + } + arg << (Q_UINT32) count; + // Parse the list for real + while (true) { + if( i > args.count() ) + { + qWarning("List end-delimiter '%s' not found.", delim.latin1()); + exit(1); + } + if( QString::fromLocal8Bit( args[ i ] ) == delim ) + break; + marshall( arg, args, i, type ); + } + } else { + qWarning( "cannot handle datatype '%s'", type.latin1() ); + exit(1); + } + i++; +. +319,346c + if ( type == "int" ) + arg << s.toInt(); + else if ( type == "uint" ) + arg << s.toUInt(); + else if ( type == "unsigned" ) + arg << s.toUInt(); + else if ( type == "unsigned int" ) + arg << s.toUInt(); + else if ( type == "long" ) + arg << s.toLong(); + else if ( type == "long int" ) + arg << s.toLong(); + else if ( type == "unsigned long" ) + arg << s.toULong(); + else if ( type == "unsigned long int" ) + arg << s.toULong(); + else if ( type == "float" ) + arg << s.toFloat(); + else if ( type == "double" ) + arg << s.toDouble(); + else if ( type == "bool" ) + arg << mkBool( s ); + else if ( type == "QString" ) + arg << s; + else if ( type == "QCString" ) + arg << QCString( args[ i ] ); + else if ( type == "QColor" ) + arg << mkColor( s ); + else if ( type == "QPoint" ) + arg << mkPoint( s ); + else if ( type == "QSize" ) + arg << mkSize( s ); + else if ( type == "QRect" ) + arg << mkRect( s ); + else if ( type == "QVariant" ) { + if ( s == "true" || s == "false" ) + arg << QVariant( mkBool( s ), 42 ); + else if ( s.left( 4 ) == "int(" ) + arg << QVariant( s.mid(4, s.length()-5).toInt() ); + else if ( s.left( 7 ) == "QPoint(" ) + arg << QVariant( mkPoint( s.mid(7, s.length()-8) ) ); + else if ( s.left( 6 ) == "QSize(" ) + arg << QVariant( mkSize( s.mid(6, s.length()-7) ) ); + else if ( s.left( 6 ) == "QRect(" ) + arg << QVariant( mkRect( s.mid(6, s.length()-7) ) ); + else if ( s.left( 7 ) == "QColor(" ) + arg << QVariant( mkColor( s.mid(7, s.length()-8) ) ); + else + arg << QVariant( s ); + } else if ( type.startsWith("QValueList<")) { + type = type.mid(11, type.length() - 12); + QStringList list; + QString delim = s; + if (delim == "[") + delim = "]"; + if (delim == "(") + delim = ")"; +. +247,317c + if (type == "QStringList") + type = "QValueList"; + if (type == "QCStringList") + type = "QValueList"; + if( i > args.count() ) + { + qWarning("Not enough arguments."); + exit(1); + } + QString s = QString::fromLocal8Bit( args[ i ] ); +. +245c +void marshall( QDataStream &arg, QCStringList args, uint &i, QString type ) +. diff --git a/kompare/tests/cvsdiff/normal.diff b/kompare/tests/cvsdiff/normal.diff new file mode 100644 index 00000000..3becb815 --- /dev/null +++ b/kompare/tests/cvsdiff/normal.diff @@ -0,0 +1,29 @@ +Index: client/dcopfind.cpp +=================================================================== +RCS file: /home/kde/kdelibs/dcop/client/dcopfind.cpp,v +retrieving revision 1.2 +diff -r1.2 dcopfind.cpp +39c39 +< bool findObject( const char* app, const char* obj, const char* func, int argc, char** args ) +--- +> bool findObject( const char* app, const char* obj, const char* func, QCStringList args ) +121c121 +< if ( (int) types.count() != argc ) { +--- +> if ( types.count() != args.count() ) { +131c131 +< marshall(arg, argc, args, i, *it); +--- +> marshall(arg, args, i, *it); +133c133 +< if ( (int) i != argc ) { +--- +> if ( (uint) i != args.count() ) { +224c224,228 +< findObject( app, objid, function, argc, args ); +--- +> QCStringList params; +> for( int i = 0; i < argc; i++ ) +> params.append( args[ i ] ); +> +> findObject( app, objid, function, params ); diff --git a/kompare/tests/cvsdiff/normalm.diff b/kompare/tests/cvsdiff/normalm.diff new file mode 100644 index 00000000..935763a0 --- /dev/null +++ b/kompare/tests/cvsdiff/normalm.diff @@ -0,0 +1,861 @@ +Index: client/dcop.cpp +=================================================================== +RCS file: /home/kde/kdelibs/dcop/client/dcop.cpp,v +retrieving revision 1.26 +diff -r1.26 dcop.cpp +23c23,26 +< #include +--- +> #include +> #include +> #include +> +25c28,39 +< #include "../kdatastream.h" +--- +> #include +> #include +> #include +> #include +> #include +> #include +> #include +> +> // putenv() is not available on all platforms, so make sure the emulation +> // wrapper is available in those cases by loading config.h! +> #include +> +28,30c42 +< #include +< #include +< #include +--- +> #include "../kdatastream.h" +33a46,47 +> typedef QMap UserList; +> +35a50,63 +> static QTextStream cout( stdout, IO_WriteOnly ); +> static QTextStream cerr( stderr, IO_WriteOnly ); +> +> /** +> * Session to send call to +> * DefaultSession - current session. Current KDE session when called without +> * --user or --all-users option. Otherwise this value ignores +> * all users with more than one active session. +> * AllSessions - Send to all sessions found. requires --user or --all-users. +> * QuerySessions - Don't call DCOP, return a list of available sessions. +> * CustomSession - Use the specified session +> */ +> enum Session { DefaultSession = 0, AllSessions, QuerySessions, CustomSession }; +> +121c149 +< void callFunction( const char* app, const char* obj, const char* func, int argc, char** args ) +--- +> void callFunction( const char* app, const char* obj, const char* func, const QCStringList args ) +123d150 +< +139c166 +< if ( !ok && argc == 0 ) +--- +> if ( !ok && args.isEmpty() ) +156,157c183,184 +< int a = (*it).contains(','); +< if ( ( a == 0 && argc == 0) || ( a > 0 && a + 1 == argc ) ) +--- +> uint a = (*it).contains(','); +> if ( ( a == 0 && args.isEmpty() ) || ( a > 0 && a + 1 == args.count() ) ) +164c191,192 +< exit(1); +--- +> // exit(1); +> return; +246,250c274,279 +< int i = 0; +< for ( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) { +< marshall(arg, argc, args, i, *it); +< } +< if ( i != argc ) { +--- +> uint i = 0; +> for( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) +> marshall( arg, args, i, *it ); +> +> if ( i != args.count() ) +> { +268a298,324 +> /** +> * Show command-line help and exit +> */ +> void showHelp( int exitCode = 0 ) +> { +> cout << "Usage: dcop [options] [application [object [function [arg1] [arg2] ... ] ] ]" << endl +> << "" << endl +> << "Console DCOP client" << endl +> << "" << endl +> << "Generic options:" << endl +> << " --help Show help about options" << endl +> << "" << endl +> << "Options:" << endl +> << " --pipe Call DCOP for each line read from stdin" << endl +> << " --user Connect to the given user's DCOP server. This option will" << endl +> << " ignore the values of the environment vars $DCOPSERVER and" << endl +> << " $ICEAUTHORITY, even if they are set." << endl +> << " If the user has more than one open session, you must also" << endl +> << " use one of the --list-sessions, --session or --als-sessions" << endl +> << " command-line options." << endl +> << " --all-users Send the same DCOP call to all users with a running DCOP" << endl +> << " server. Only failed calls to existing DCOP servers will" +> << " generate an error message. If no DCOP server is available" << endl +> << " at all, no error will be generated." << endl; +> +> exit( exitCode ); +> } +270,271c326,330 +< +< int main( int argc, char** argv ) +--- +> /** +> * Return a list of all users and their home directories. +> * Returns an empty list if /etc/passwd cannot be read for some reason. +> */ +> static UserList userList() +272a332,340 +> UserList result; +> +> QFile f( "/etc/passwd" ); +> +> if( !f.open( IO_ReadOnly ) ) +> { +> cerr << "Can't open /etc/passwd for reading!" << endl; +> return result; +> } +274,276c342,347 +< if ( argc > 1 && argv[1][0] == '-' ) { +< fprintf( stderr, "Usage: dcop [ application [object [function [arg1] [arg2] [arg3] ... ] ] ] \n" ); +< exit(0); +--- +> QStringList l( QStringList::split( '\n', f.readAll() ) ); +> +> for( QStringList::ConstIterator it( l.begin() ); it != l.end(); ++it ) +> { +> QStringList userInfo( QStringList::split( ':', *it, true ) ); +> result[ userInfo[ 0 ] ] = userInfo[ 5 ]; +279,281c350,391 +< DCOPClient client; +< client.attach(); +< dcop = &client; +--- +> return result; +> } +> +> /** +> * Return a list of available DCOP sessions for the specified user +> * An empty list means no sessions are available, or an error occurred. +> */ +> QStringList dcopSessionList( const QString &user, const QString &home ) +> { +> if( home.isEmpty() ) +> { +> cerr << "WARNING: Cannot determine home directory for user " +> << user << "!" << endl +> << "Please check permissions or set the $DCOPSERVER variable manually before" << endl +> << "calling dcop." << endl; +> return QStringList(); +> } +> +> QStringList result; +> QFileInfo dirInfo( home ); +> if( !dirInfo.exists() || !dirInfo.isReadable() ) +> return result; +> +> QDir d( home ); +> d.setFilter( QDir::Files | QDir::Hidden | QDir::NoSymLinks ); +> d.setNameFilter( ".DCOPserver*" ); +> +> const QFileInfoList *list = d.entryInfoList(); +> if( !list ) +> return result; +> +> QFileInfoListIterator it( *list ); +> QFileInfo *fi; +> +> while ( ( fi = it.current() ) != 0 ) +> { +> if( fi->isReadable() ) +> result.append( fi->fileName() ); +> ++it; +> } +> return result; +> } +282a393,398 +> /** +> * Do the actual DCOP call +> */ +> void runDCOP( QCStringList args, UserList users, Session session, +> const QString sessionName, bool readStdin ) +> { +286,287c402,404 +< char **args = 0; +< if ((argc > 1) && (strncmp(argv[1], "DCOPRef(", 8)) == 0) +--- +> QCStringList params; +> DCOPClient *client = 0L; +> if ( !args.isEmpty() && args[ 0 ].find( "DCOPRef(" ) == 0 ) +289,304c406,429 +< char *delim = strchr(argv[1], ','); +< if (!delim) +< { +< fprintf(stderr, "Error: '%s' is not a valid DCOP reference.\n", argv[1]); +< return 1; +< } +< *delim = 0; +< app = argv[1] + 8; +< delim++; +< delim[strlen(delim)-1] = 0; +< objid = delim; +< if (argc > 2) +< function = argv[2]; +< if (argc > 3) +< args = &argv[3]; +< argc++; +--- +> // WARNING: This part (until the closing '}') could very +> // well be broken now. As I don't know how to trigger and test +> // dcoprefs this code is *not* tested. It compiles and it looks +> // ok to me, but that's all I can say - Martijn (2001/12/24) +> int delimPos = args[ 0 ].findRev( ',' ); +> if( delimPos == -1 ) +> { +> cerr << "Error: '" << args[ 0 ] +> << "' is not a valid DCOP reference." << endl; +> exit( -1 ); +> } +> args[ 0 ][ delimPos ] = 0; +> app = args[ 0 ].mid( 8 ); +> delimPos++; +> args[ 0 ][ args[ 0 ].length() - 1 ] = 0; +> objid = args[ 0 ].mid( delimPos ); +> if( args.count() > 1 ) +> function = args[ 1 ]; +> if( args.count() > 2 ) +> { +> params = args; +> params.remove( params.begin() ); +> params.remove( params.begin() ); +> } +308,338c433,516 +< if (argc > 1) +< app = argv[1]; +< if (argc > 2) +< objid = argv[2]; +< if (argc > 3) +< function = argv[3]; +< if (argc > 4) +< args = &argv[4]; +< } +< +< switch ( argc ) { +< case 0: +< case 1: +< queryApplications(""); +< break; +< case 2: +< if (endsWith(app, '*')) +< queryApplications(app); +< else +< queryObjects( app, "" ); +< break; +< case 3: +< if (endsWith(objid, '*')) +< queryObjects(app, objid); +< else +< queryFunctions( app, objid ); +< break; +< case 4: +< default: +< callFunction( app, objid, function, argc - 4, args ); +< break; +--- +> if( !args.isEmpty() ) +> app = args[ 0 ]; +> if( args.count() > 1 ) +> objid = args[ 1 ]; +> if( args.count() > 2 ) +> function = args[ 2 ]; +> if( args.count() > 3) +> { +> params = args; +> params.remove( params.begin() ); +> params.remove( params.begin() ); +> params.remove( params.begin() ); +> } +> } +> +> bool firstRun = true; +> UserList::Iterator it; +> QStringList sessions; +> bool presetDCOPServer = false; +> // char *dcopStr = 0L; +> QString dcopServer; +> +> for( it = users.begin(); it != users.end() || firstRun; it++ ) +> { +> firstRun = false; +> +> //cout << "Iterating '" << it.key() << "'" << endl; +> +> if( session == QuerySessions ) +> { +> QStringList sessions = dcopSessionList( it.key(), it.data() ); +> if( sessions.isEmpty() ) +> { +> cout << "No active sessions"; +> if( !( *it ).isEmpty() ) +> cout << " for user " << *it; +> cout << endl; +> } +> else +> { +> cout << "Active sessions "; +> if( !( *it ).isEmpty() ) +> cout << "for user " << *it << " "; +> cout << ":" << endl; +> +> QStringList::Iterator sIt; +> for( sIt = sessions.begin(); sIt != sessions.end(); sIt++ ) +> cout << " " << *sIt << endl; +> +> cout << endl; +> } +> continue; +> } +> +> if( getenv( "DCOPSERVER" ) ) +> { +> sessions.append( getenv( "DCOPSERVER" ) ); +> presetDCOPServer = true; +> } +> +> if( users.count() > 1 || ( users.count() == 1 && +> ( getenv( "DCOPSERVER" ) == 0 /*&& getenv( "DISPLAY" ) == 0*/ ) ) ) +> { +> sessions = dcopSessionList( it.key(), it.data() ); +> if( sessions.isEmpty() ) +> { +> if( users.count() > 1 ) +> continue; +> else +> { +> cerr << "ERROR: No active KDE sessions!" << endl +> << "If you are sure there is one, please set the $DCOPSERVER variable manually" << endl +> << "before calling dcop." << endl; +> exit( -1 ); +> } +> } +> else if( sessions.count() > 1 && session != AllSessions ) +> { +> cerr << "ERROR: Multiple available KDE sessions!" << endl +> << "Please specify the correct session to use with --session or use the" << endl +> << "--all-sessions option to broadcast to all sessions." << endl; +> exit( -1 ); +> } +> } +339a518,660 +> if( users.count() > 1 || ( users.count() == 1 && +> ( getenv( "ICEAUTHORITY" ) == 0 || getenv( "DISPLAY" ) == 0 ) ) ) +> { +> // Check for ICE authority file and if the file can be read by us +> QString home = it.data(); +> QString iceFile = it.data() + "/.ICEauthority"; +> QFileInfo fi( iceFile ); +> if( iceFile.isEmpty() ) +> { +> cerr << "WARNING: Cannot determine home directory for user " +> << it.key() << "!" << endl +> << "Please check permissions or set the $ICEAUTHORITY variable manually before" << endl +> << "calling dcop." << endl; +> } +> else if( fi.exists() ) +> { +> if( fi.isReadable() ) +> { +> char *envStr = strdup( ( "ICEAUTHORITY=" + iceFile ).ascii() ); +> putenv( envStr ); +> //cerr << "ice: " << envStr << endl; +> } +> else +> { +> cerr << "WARNING: ICE authority file " << iceFile +> << "is not readable by you!" << endl +> << "Please check permissions or set the $ICEAUTHORITY variable manually before" << endl +> << "calling dcop." << endl; +> } +> } +> else +> { +> if( users.count() > 1 ) +> continue; +> else +> { +> cerr << "WARNING: Cannot find ICE authority file " +> << iceFile << "!" << endl +> << "Please check permissions or set the $ICEAUTHORITY" +> << " variable manually before" << endl +> << "calling dcop." << endl; +> } +> } +> } +> +> // Main loop +> // If users is an empty list we're calling for the currently logged +> // in user. In this case we don't have a session, but still want +> // to iterate the loop once. +> QStringList::Iterator sIt = sessions.begin(); +> for( ; sIt != sessions.end() || users.isEmpty(); sIt++ ) +> { +> if( !presetDCOPServer && !users.isEmpty() ) +> { +> QString dcopFile = it.data() + "/" + *sIt; +> QFile f( dcopFile ); +> if( !f.open( IO_ReadOnly ) ) +> { +> cerr << "Can't open " << dcopFile << " for reading!" << endl; +> exit( -1 ); +> } +> +> QStringList l( QStringList::split( '\n', f.readAll() ) ); +> dcopServer = l.first(); +> +> if( dcopServer.isEmpty() ) +> { +> cerr << "WARNING: Unable to determine DCOP server for session " +> << *sIt << "!" << endl +> << "Please check permissions or set the $DCOPSERVER variable manually before" << endl +> << "calling dcop." << endl; +> exit( -1 ); +> } +> } +> +> delete client; +> client = new DCOPClient; +> if( !dcopServer.isEmpty() ) +> client->setServerAddress( dcopServer.ascii() ); +> bool success = client->attach(); +> if( !success ) +> { +> cerr << "ERROR: Couldn't attach to DCOP server!" << endl; +> continue; +> } +> dcop = client; +> +> switch ( args.count() ) +> { +> case 0: +> queryApplications(""); +> break; +> case 1: +> if (endsWith(app, '*')) +> queryApplications(app); +> else +> queryObjects( app, "" ); +> break; +> case 2: +> if (endsWith(objid, '*')) +> queryObjects(app, objid); +> else +> queryFunctions( app, objid ); +> break; +> case 3: +> default: +> if( readStdin ) +> { +> QCStringList::Iterator replaceArg = args.end(); +> +> QCStringList::Iterator it; +> for( it = args.begin(); it != args.end(); it++ ) +> if( *it == "%1" ) +> replaceArg = it; +> +> // Read from stdin until EOF and call function for each line read +> char *buf = new char[ 1000 ]; +> while ( !feof( stdin ) ) +> { +> fgets( buf, 1000, stdin ); +> +> if( replaceArg != args.end() ) +> *replaceArg = buf; +> +> callFunction( app, objid, function, params ); +> } +> } +> else +> { +> // Just call function +> // cout << "call " << app << ", " << objid << ", " << function << ", (params)" << endl; +> callFunction( app, objid, function, params ); +> } +> break; +> } +> // Another sIt++ would make the loop infinite... +> if( users.isEmpty() ) +> break; +> } +> +> // Another it++ would make the loop infinite... +> if( it == users.end() ) +> break; +340a662,767 +> } +> +> +> int main( int argc, char** argv ) +> { +> bool readStdin = false; +> int numOptions = 0; +> QString user; +> Session session = DefaultSession; +> QString sessionName; +> +> // Scan for command-line options first +> for( int pos = 1 ; pos <= argc - 1 ; pos++ ) +> { +> if( strcmp( argv[ pos ], "--help" ) == 0 ) +> showHelp( 0 ); +> else if( strcmp( argv[ pos ], "--pipe" ) == 0 ) +> { +> readStdin = true; +> numOptions++; +> } +> else if( strcmp( argv[ pos ], "--user" ) == 0 ) +> { +> if( pos <= argc - 2 ) +> { +> user = QString::fromLocal8Bit( argv[ pos + 1] ); +> numOptions +=2; +> pos++; +> } +> else +> { +> cerr << "Missing username for '--user' option!" << endl << endl; +> showHelp( -1 ); +> } +> } +> else if( strcmp( argv[ pos ], "--all-users" ) == 0 ) +> { +> user = "*"; +> numOptions ++; +> } +> else if( strcmp( argv[ pos ], "--list-sessions" ) == 0 ) +> { +> session = QuerySessions; +> numOptions ++; +> } +> else if( strcmp( argv[ pos ], "--all-sessions" ) == 0 ) +> { +> session = AllSessions; +> numOptions ++; +> } +> else if( argv[ pos ][ 0 ] == '-' ) +> { +> cerr << "Unknown command-line option '" << argv[ pos ] +> << "'." << endl << endl; +> showHelp( -1 ); +> } +> else +> break; // End of options +> } +> +> argc -= numOptions; +> +> QCStringList args; +> for( int i = numOptions; i < argc + numOptions - 1; i++ ) +> args.append( argv[ i + 1 ] ); +> +> if( readStdin && args.count() < 3 ) +> { +> cerr << "--pipe option only supported for function calls!" << endl << endl; +> showHelp( -1 ); +> } +> +> if( user == "*" && args.count() < 3 && session != QuerySessions ) +> { +> cerr << "ERROR: The --all-users option is only supported for function calls!" << endl << endl; +> showHelp( -1 ); +> } +> +> if( session == QuerySessions && !args.isEmpty() ) +> { +> cerr << "ERROR: The --list-sessions option cannot be used for actual DCOP calls!" << endl << endl; +> showHelp( -1 ); +> } +> +> if( session == QuerySessions && user.isEmpty() ) +> { +> cerr << "ERROR: The --list-sessions option can only be used with the --user or" << endl +> << "--all-users options!" << endl << endl; +> showHelp( -1 ); +> } +> +> if( session != DefaultSession && session != QuerySessions && +> args.count() < 3 ) +> { +> cerr << "ERROR: The --session and --all-sessions options are only supported for function" << endl +> << "calls!" << endl << endl; +> showHelp( -1 ); +> } +> +> UserList users; +> if( user == "*" ) +> users = userList(); +> else if( !user.isEmpty() ) +> users[ user ] = userList()[ user ]; +> +> runDCOP( args, users, session, sessionName, readStdin ); +343a771,773 +> +> // vim: set ts=8 sts=4 sw=4 noet: +> +Index: client/dcopfind.cpp +=================================================================== +RCS file: /home/kde/kdelibs/dcop/client/dcopfind.cpp,v +retrieving revision 1.2 +diff -r1.2 dcopfind.cpp +39c39 +< bool findObject( const char* app, const char* obj, const char* func, int argc, char** args ) +--- +> bool findObject( const char* app, const char* obj, const char* func, QCStringList args ) +121c121 +< if ( (int) types.count() != argc ) { +--- +> if ( types.count() != args.count() ) { +131c131 +< marshall(arg, argc, args, i, *it); +--- +> marshall(arg, args, i, *it); +133c133 +< if ( (int) i != argc ) { +--- +> if ( (uint) i != args.count() ) { +224c224,228 +< findObject( app, objid, function, argc, args ); +--- +> QCStringList params; +> for( int i = 0; i < argc; i++ ) +> params.append( args[ i ] ); +> +> findObject( app, objid, function, params ); +Index: client/marshall.cpp +=================================================================== +RCS file: /home/kde/kdelibs/dcop/client/marshall.cpp,v +retrieving revision 1.3 +diff -r1.3 marshall.cpp +245c245 +< void marshall(QDataStream &arg, int argc, char **argv, int &i, QString type) +--- +> void marshall( QDataStream &arg, QCStringList args, uint &i, QString type ) +247,317c247,256 +< if (type == "QStringList") +< type = "QValueList"; +< if (type == "QCStringList") +< type = "QValueList"; +< if (i >= argc) +< { +< qWarning("Not enough arguments."); +< exit(1); +< } +< QString s = QString::fromLocal8Bit(argv[i]); +< +< if ( type == "int" ) +< arg << s.toInt(); +< else if ( type == "uint" ) +< arg << s.toUInt(); +< else if ( type == "unsigned" ) +< arg << s.toUInt(); +< else if ( type == "unsigned int" ) +< arg << s.toUInt(); +< else if ( type == "long" ) +< arg << s.toLong(); +< else if ( type == "long int" ) +< arg << s.toLong(); +< else if ( type == "unsigned long" ) +< arg << s.toULong(); +< else if ( type == "unsigned long int" ) +< arg << s.toULong(); +< else if ( type == "float" ) +< arg << s.toFloat(); +< else if ( type == "double" ) +< arg << s.toDouble(); +< else if ( type == "bool" ) +< arg << mkBool( s ); +< else if ( type == "QString" ) +< arg << s; +< else if ( type == "QCString" ) +< arg << QCString( argv[i] ); +< else if ( type == "QColor" ) +< arg << mkColor( s ); +< else if ( type == "QPoint" ) +< arg << mkPoint( s ); +< else if ( type == "QSize" ) +< arg << mkSize( s ); +< else if ( type == "QRect" ) +< arg << mkRect( s ); +< else if ( type == "QVariant" ) { +< if ( s == "true" || s == "false" ) +< arg << QVariant( mkBool( s ), 42 ); +< else if ( s.left( 4 ) == "int(" ) +< arg << QVariant( s.mid(4, s.length()-5).toInt() ); +< else if ( s.left( 7 ) == "QPoint(" ) +< arg << QVariant( mkPoint( s.mid(7, s.length()-8) ) ); +< else if ( s.left( 6 ) == "QSize(" ) +< arg << QVariant( mkSize( s.mid(6, s.length()-7) ) ); +< else if ( s.left( 6 ) == "QRect(" ) +< arg << QVariant( mkRect( s.mid(6, s.length()-7) ) ); +< else if ( s.left( 7 ) == "QColor(" ) +< arg << QVariant( mkColor( s.mid(7, s.length()-8) ) ); +< else +< arg << QVariant( s ); +< } else if ( type.startsWith("QValueList<")) { +< type = type.mid(11, type.length() - 12); +< QStringList list; +< QString delim = s; +< if (delim == "[") +< delim = "]"; +< if (delim == "(") +< delim = ")"; +< i++; +< QByteArray dummy_data; +< QDataStream dummy_arg(dummy_data, IO_WriteOnly); +--- +> if (type == "QStringList") +> type = "QValueList"; +> if (type == "QCStringList") +> type = "QValueList"; +> if( i > args.count() ) +> { +> qWarning("Not enough arguments."); +> exit(1); +> } +> QString s = QString::fromLocal8Bit( args[ i ] ); +319,346c258,314 +< int j = i; +< int count = 0; +< // Parse list to get the count +< while (true) { +< if (j >= argc) +< { +< qWarning("List end-delimiter '%s' not found.", delim.latin1()); +< exit(1); +< } +< if (argv[j] == delim) break; +< marshall(dummy_arg, argc, argv, j, type); +< count++; +< } +< arg << (Q_UINT32) count; +< // Parse the list for real +< while (true) { +< if (i >= argc) +< { +< qWarning("List end-delimiter '%s' not found.", delim.latin1()); +< exit(1); +< } +< if (argv[i] == delim) break; +< marshall(arg, argc, argv, i, type); +< } +< } else { +< qWarning( "cannot handle datatype '%s'", type.latin1() ); +< exit(1); +< } +--- +> if ( type == "int" ) +> arg << s.toInt(); +> else if ( type == "uint" ) +> arg << s.toUInt(); +> else if ( type == "unsigned" ) +> arg << s.toUInt(); +> else if ( type == "unsigned int" ) +> arg << s.toUInt(); +> else if ( type == "long" ) +> arg << s.toLong(); +> else if ( type == "long int" ) +> arg << s.toLong(); +> else if ( type == "unsigned long" ) +> arg << s.toULong(); +> else if ( type == "unsigned long int" ) +> arg << s.toULong(); +> else if ( type == "float" ) +> arg << s.toFloat(); +> else if ( type == "double" ) +> arg << s.toDouble(); +> else if ( type == "bool" ) +> arg << mkBool( s ); +> else if ( type == "QString" ) +> arg << s; +> else if ( type == "QCString" ) +> arg << QCString( args[ i ] ); +> else if ( type == "QColor" ) +> arg << mkColor( s ); +> else if ( type == "QPoint" ) +> arg << mkPoint( s ); +> else if ( type == "QSize" ) +> arg << mkSize( s ); +> else if ( type == "QRect" ) +> arg << mkRect( s ); +> else if ( type == "QVariant" ) { +> if ( s == "true" || s == "false" ) +> arg << QVariant( mkBool( s ), 42 ); +> else if ( s.left( 4 ) == "int(" ) +> arg << QVariant( s.mid(4, s.length()-5).toInt() ); +> else if ( s.left( 7 ) == "QPoint(" ) +> arg << QVariant( mkPoint( s.mid(7, s.length()-8) ) ); +> else if ( s.left( 6 ) == "QSize(" ) +> arg << QVariant( mkSize( s.mid(6, s.length()-7) ) ); +> else if ( s.left( 6 ) == "QRect(" ) +> arg << QVariant( mkRect( s.mid(6, s.length()-7) ) ); +> else if ( s.left( 7 ) == "QColor(" ) +> arg << QVariant( mkColor( s.mid(7, s.length()-8) ) ); +> else +> arg << QVariant( s ); +> } else if ( type.startsWith("QValueList<")) { +> type = type.mid(11, type.length() - 12); +> QStringList list; +> QString delim = s; +> if (delim == "[") +> delim = "]"; +> if (delim == "(") +> delim = ")"; +347a316,349 +> QByteArray dummy_data; +> QDataStream dummy_arg(dummy_data, IO_WriteOnly); +> +> uint j = i; +> uint count = 0; +> // Parse list to get the count +> while (true) { +> if( j > args.count() ) +> { +> qWarning("List end-delimiter '%s' not found.", delim.latin1()); +> exit(1); +> } +> if( QString::fromLocal8Bit( args[ j ] ) == delim ) +> break; +> marshall( dummy_arg, args, j, type ); +> count++; +> } +> arg << (Q_UINT32) count; +> // Parse the list for real +> while (true) { +> if( i > args.count() ) +> { +> qWarning("List end-delimiter '%s' not found.", delim.latin1()); +> exit(1); +> } +> if( QString::fromLocal8Bit( args[ i ] ) == delim ) +> break; +> marshall( arg, args, i, type ); +> } +> } else { +> qWarning( "cannot handle datatype '%s'", type.latin1() ); +> exit(1); +> } +> i++; diff --git a/kompare/tests/cvsdiff/rcs.diff b/kompare/tests/cvsdiff/rcs.diff new file mode 100644 index 00000000..da42d91a --- /dev/null +++ b/kompare/tests/cvsdiff/rcs.diff @@ -0,0 +1,24 @@ +Index: client/dcopfind.cpp +=================================================================== +RCS file: /home/kde/kdelibs/dcop/client/dcopfind.cpp,v +retrieving revision 1.2 +diff -n -r1.2 dcopfind.cpp +d39 1 +a39 1 +bool findObject( const char* app, const char* obj, const char* func, QCStringList args ) +d121 1 +a121 1 + if ( types.count() != args.count() ) { +d131 1 +a131 1 + marshall(arg, args, i, *it); +d133 1 +a133 1 + if ( (uint) i != args.count() ) { +d224 1 +a224 5 + QCStringList params; + for( int i = 0; i < argc; i++ ) + params.append( args[ i ] ); + + findObject( app, objid, function, params ); diff --git a/kompare/tests/cvsdiff/rcsm.diff b/kompare/tests/cvsdiff/rcsm.diff new file mode 100644 index 00000000..c690b2a7 --- /dev/null +++ b/kompare/tests/cvsdiff/rcsm.diff @@ -0,0 +1,683 @@ +Index: client/dcop.cpp +=================================================================== +RCS file: /home/kde/kdelibs/dcop/client/dcop.cpp,v +retrieving revision 1.26 +diff -n -r1.26 dcop.cpp +d23 1 +a23 4 +#include +#include +#include + +d25 1 +a25 12 +#include +#include +#include +#include +#include +#include +#include + +// putenv() is not available on all platforms, so make sure the emulation +// wrapper is available in those cases by loading config.h! +#include + +d28 3 +a30 1 +#include "../kdatastream.h" +a33 2 +typedef QMap UserList; + +a35 14 +static QTextStream cout( stdout, IO_WriteOnly ); +static QTextStream cerr( stderr, IO_WriteOnly ); + +/** + * Session to send call to + * DefaultSession - current session. Current KDE session when called without + * --user or --all-users option. Otherwise this value ignores + * all users with more than one active session. + * AllSessions - Send to all sessions found. requires --user or --all-users. + * QuerySessions - Don't call DCOP, return a list of available sessions. + * CustomSession - Use the specified session + */ +enum Session { DefaultSession = 0, AllSessions, QuerySessions, CustomSession }; + +d121 1 +a121 1 +void callFunction( const char* app, const char* obj, const char* func, const QCStringList args ) +d123 1 +d139 1 +a139 1 + if ( !ok && args.isEmpty() ) +d156 2 +a157 2 + uint a = (*it).contains(','); + if ( ( a == 0 && args.isEmpty() ) || ( a > 0 && a + 1 == args.count() ) ) +d164 1 +a164 2 +// exit(1); + return; +d246 5 +a250 6 + uint i = 0; + for( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) + marshall( arg, args, i, *it ); + + if ( i != args.count() ) + { +a268 27 +/** + * Show command-line help and exit + */ +void showHelp( int exitCode = 0 ) +{ + cout << "Usage: dcop [options] [application [object [function [arg1] [arg2] ... ] ] ]" << endl + << "" << endl + << "Console DCOP client" << endl + << "" << endl + << "Generic options:" << endl + << " --help Show help about options" << endl + << "" << endl + << "Options:" << endl + << " --pipe Call DCOP for each line read from stdin" << endl + << " --user Connect to the given user's DCOP server. This option will" << endl + << " ignore the values of the environment vars $DCOPSERVER and" << endl + << " $ICEAUTHORITY, even if they are set." << endl + << " If the user has more than one open session, you must also" << endl + << " use one of the --list-sessions, --session or --als-sessions" << endl + << " command-line options." << endl + << " --all-users Send the same DCOP call to all users with a running DCOP" << endl + << " server. Only failed calls to existing DCOP servers will" + << " generate an error message. If no DCOP server is available" << endl + << " at all, no error will be generated." << endl; + + exit( exitCode ); +} +d270 2 +a271 5 +/** + * Return a list of all users and their home directories. + * Returns an empty list if /etc/passwd cannot be read for some reason. + */ +static UserList userList() +a272 9 + UserList result; + + QFile f( "/etc/passwd" ); + + if( !f.open( IO_ReadOnly ) ) + { + cerr << "Can't open /etc/passwd for reading!" << endl; + return result; + } +d274 3 +a276 6 + QStringList l( QStringList::split( '\n', f.readAll() ) ); + + for( QStringList::ConstIterator it( l.begin() ); it != l.end(); ++it ) + { + QStringList userInfo( QStringList::split( ':', *it, true ) ); + result[ userInfo[ 0 ] ] = userInfo[ 5 ]; +d279 3 +a281 42 + return result; +} + +/** + * Return a list of available DCOP sessions for the specified user + * An empty list means no sessions are available, or an error occurred. + */ +QStringList dcopSessionList( const QString &user, const QString &home ) +{ + if( home.isEmpty() ) + { + cerr << "WARNING: Cannot determine home directory for user " + << user << "!" << endl + << "Please check permissions or set the $DCOPSERVER variable manually before" << endl + << "calling dcop." << endl; + return QStringList(); + } + + QStringList result; + QFileInfo dirInfo( home ); + if( !dirInfo.exists() || !dirInfo.isReadable() ) + return result; + + QDir d( home ); + d.setFilter( QDir::Files | QDir::Hidden | QDir::NoSymLinks ); + d.setNameFilter( ".DCOPserver*" ); + + const QFileInfoList *list = d.entryInfoList(); + if( !list ) + return result; + + QFileInfoListIterator it( *list ); + QFileInfo *fi; + + while ( ( fi = it.current() ) != 0 ) + { + if( fi->isReadable() ) + result.append( fi->fileName() ); + ++it; + } + return result; +} +a282 6 +/** + * Do the actual DCOP call + */ +void runDCOP( QCStringList args, UserList users, Session session, + const QString sessionName, bool readStdin ) +{ +d286 2 +a287 3 + QCStringList params; + DCOPClient *client = 0L; + if ( !args.isEmpty() && args[ 0 ].find( "DCOPRef(" ) == 0 ) +d289 16 +a304 24 + // WARNING: This part (until the closing '}') could very + // well be broken now. As I don't know how to trigger and test + // dcoprefs this code is *not* tested. It compiles and it looks + // ok to me, but that's all I can say - Martijn (2001/12/24) + int delimPos = args[ 0 ].findRev( ',' ); + if( delimPos == -1 ) + { + cerr << "Error: '" << args[ 0 ] + << "' is not a valid DCOP reference." << endl; + exit( -1 ); + } + args[ 0 ][ delimPos ] = 0; + app = args[ 0 ].mid( 8 ); + delimPos++; + args[ 0 ][ args[ 0 ].length() - 1 ] = 0; + objid = args[ 0 ].mid( delimPos ); + if( args.count() > 1 ) + function = args[ 1 ]; + if( args.count() > 2 ) + { + params = args; + params.remove( params.begin() ); + params.remove( params.begin() ); + } +d308 31 +a338 84 + if( !args.isEmpty() ) + app = args[ 0 ]; + if( args.count() > 1 ) + objid = args[ 1 ]; + if( args.count() > 2 ) + function = args[ 2 ]; + if( args.count() > 3) + { + params = args; + params.remove( params.begin() ); + params.remove( params.begin() ); + params.remove( params.begin() ); + } + } + + bool firstRun = true; + UserList::Iterator it; + QStringList sessions; + bool presetDCOPServer = false; +// char *dcopStr = 0L; + QString dcopServer; + + for( it = users.begin(); it != users.end() || firstRun; it++ ) + { + firstRun = false; + + //cout << "Iterating '" << it.key() << "'" << endl; + + if( session == QuerySessions ) + { + QStringList sessions = dcopSessionList( it.key(), it.data() ); + if( sessions.isEmpty() ) + { + cout << "No active sessions"; + if( !( *it ).isEmpty() ) + cout << " for user " << *it; + cout << endl; + } + else + { + cout << "Active sessions "; + if( !( *it ).isEmpty() ) + cout << "for user " << *it << " "; + cout << ":" << endl; + + QStringList::Iterator sIt; + for( sIt = sessions.begin(); sIt != sessions.end(); sIt++ ) + cout << " " << *sIt << endl; + + cout << endl; + } + continue; + } + + if( getenv( "DCOPSERVER" ) ) + { + sessions.append( getenv( "DCOPSERVER" ) ); + presetDCOPServer = true; + } + + if( users.count() > 1 || ( users.count() == 1 && + ( getenv( "DCOPSERVER" ) == 0 /*&& getenv( "DISPLAY" ) == 0*/ ) ) ) + { + sessions = dcopSessionList( it.key(), it.data() ); + if( sessions.isEmpty() ) + { + if( users.count() > 1 ) + continue; + else + { + cerr << "ERROR: No active KDE sessions!" << endl + << "If you are sure there is one, please set the $DCOPSERVER variable manually" << endl + << "before calling dcop." << endl; + exit( -1 ); + } + } + else if( sessions.count() > 1 && session != AllSessions ) + { + cerr << "ERROR: Multiple available KDE sessions!" << endl + << "Please specify the correct session to use with --session or use the" << endl + << "--all-sessions option to broadcast to all sessions." << endl; + exit( -1 ); + } + } +a339 143 + if( users.count() > 1 || ( users.count() == 1 && + ( getenv( "ICEAUTHORITY" ) == 0 || getenv( "DISPLAY" ) == 0 ) ) ) + { + // Check for ICE authority file and if the file can be read by us + QString home = it.data(); + QString iceFile = it.data() + "/.ICEauthority"; + QFileInfo fi( iceFile ); + if( iceFile.isEmpty() ) + { + cerr << "WARNING: Cannot determine home directory for user " + << it.key() << "!" << endl + << "Please check permissions or set the $ICEAUTHORITY variable manually before" << endl + << "calling dcop." << endl; + } + else if( fi.exists() ) + { + if( fi.isReadable() ) + { + char *envStr = strdup( ( "ICEAUTHORITY=" + iceFile ).ascii() ); + putenv( envStr ); + //cerr << "ice: " << envStr << endl; + } + else + { + cerr << "WARNING: ICE authority file " << iceFile + << "is not readable by you!" << endl + << "Please check permissions or set the $ICEAUTHORITY variable manually before" << endl + << "calling dcop." << endl; + } + } + else + { + if( users.count() > 1 ) + continue; + else + { + cerr << "WARNING: Cannot find ICE authority file " + << iceFile << "!" << endl + << "Please check permissions or set the $ICEAUTHORITY" + << " variable manually before" << endl + << "calling dcop." << endl; + } + } + } + + // Main loop + // If users is an empty list we're calling for the currently logged + // in user. In this case we don't have a session, but still want + // to iterate the loop once. + QStringList::Iterator sIt = sessions.begin(); + for( ; sIt != sessions.end() || users.isEmpty(); sIt++ ) + { + if( !presetDCOPServer && !users.isEmpty() ) + { + QString dcopFile = it.data() + "/" + *sIt; + QFile f( dcopFile ); + if( !f.open( IO_ReadOnly ) ) + { + cerr << "Can't open " << dcopFile << " for reading!" << endl; + exit( -1 ); + } + + QStringList l( QStringList::split( '\n', f.readAll() ) ); + dcopServer = l.first(); + + if( dcopServer.isEmpty() ) + { + cerr << "WARNING: Unable to determine DCOP server for session " + << *sIt << "!" << endl + << "Please check permissions or set the $DCOPSERVER variable manually before" << endl + << "calling dcop." << endl; + exit( -1 ); + } + } + + delete client; + client = new DCOPClient; + if( !dcopServer.isEmpty() ) + client->setServerAddress( dcopServer.ascii() ); + bool success = client->attach(); + if( !success ) + { + cerr << "ERROR: Couldn't attach to DCOP server!" << endl; + continue; + } + dcop = client; + + switch ( args.count() ) + { + case 0: + queryApplications(""); + break; + case 1: + if (endsWith(app, '*')) + queryApplications(app); + else + queryObjects( app, "" ); + break; + case 2: + if (endsWith(objid, '*')) + queryObjects(app, objid); + else + queryFunctions( app, objid ); + break; + case 3: + default: + if( readStdin ) + { + QCStringList::Iterator replaceArg = args.end(); + + QCStringList::Iterator it; + for( it = args.begin(); it != args.end(); it++ ) + if( *it == "%1" ) + replaceArg = it; + + // Read from stdin until EOF and call function for each line read + char *buf = new char[ 1000 ]; + while ( !feof( stdin ) ) + { + fgets( buf, 1000, stdin ); + + if( replaceArg != args.end() ) + *replaceArg = buf; + + callFunction( app, objid, function, params ); + } + } + else + { + // Just call function +// cout << "call " << app << ", " << objid << ", " << function << ", (params)" << endl; + callFunction( app, objid, function, params ); + } + break; + } + // Another sIt++ would make the loop infinite... + if( users.isEmpty() ) + break; + } + + // Another it++ would make the loop infinite... + if( it == users.end() ) + break; +a340 106 +} + + +int main( int argc, char** argv ) +{ + bool readStdin = false; + int numOptions = 0; + QString user; + Session session = DefaultSession; + QString sessionName; + + // Scan for command-line options first + for( int pos = 1 ; pos <= argc - 1 ; pos++ ) + { + if( strcmp( argv[ pos ], "--help" ) == 0 ) + showHelp( 0 ); + else if( strcmp( argv[ pos ], "--pipe" ) == 0 ) + { + readStdin = true; + numOptions++; + } + else if( strcmp( argv[ pos ], "--user" ) == 0 ) + { + if( pos <= argc - 2 ) + { + user = QString::fromLocal8Bit( argv[ pos + 1] ); + numOptions +=2; + pos++; + } + else + { + cerr << "Missing username for '--user' option!" << endl << endl; + showHelp( -1 ); + } + } + else if( strcmp( argv[ pos ], "--all-users" ) == 0 ) + { + user = "*"; + numOptions ++; + } + else if( strcmp( argv[ pos ], "--list-sessions" ) == 0 ) + { + session = QuerySessions; + numOptions ++; + } + else if( strcmp( argv[ pos ], "--all-sessions" ) == 0 ) + { + session = AllSessions; + numOptions ++; + } + else if( argv[ pos ][ 0 ] == '-' ) + { + cerr << "Unknown command-line option '" << argv[ pos ] + << "'." << endl << endl; + showHelp( -1 ); + } + else + break; // End of options + } + + argc -= numOptions; + + QCStringList args; + for( int i = numOptions; i < argc + numOptions - 1; i++ ) + args.append( argv[ i + 1 ] ); + + if( readStdin && args.count() < 3 ) + { + cerr << "--pipe option only supported for function calls!" << endl << endl; + showHelp( -1 ); + } + + if( user == "*" && args.count() < 3 && session != QuerySessions ) + { + cerr << "ERROR: The --all-users option is only supported for function calls!" << endl << endl; + showHelp( -1 ); + } + + if( session == QuerySessions && !args.isEmpty() ) + { + cerr << "ERROR: The --list-sessions option cannot be used for actual DCOP calls!" << endl << endl; + showHelp( -1 ); + } + + if( session == QuerySessions && user.isEmpty() ) + { + cerr << "ERROR: The --list-sessions option can only be used with the --user or" << endl + << "--all-users options!" << endl << endl; + showHelp( -1 ); + } + + if( session != DefaultSession && session != QuerySessions && + args.count() < 3 ) + { + cerr << "ERROR: The --session and --all-sessions options are only supported for function" << endl + << "calls!" << endl << endl; + showHelp( -1 ); + } + + UserList users; + if( user == "*" ) + users = userList(); + else if( !user.isEmpty() ) + users[ user ] = userList()[ user ]; + + runDCOP( args, users, session, sessionName, readStdin ); +a343 3 + +// vim: set ts=8 sts=4 sw=4 noet: + +Index: client/dcopfind.cpp +=================================================================== +RCS file: /home/kde/kdelibs/dcop/client/dcopfind.cpp,v +retrieving revision 1.2 +diff -n -r1.2 dcopfind.cpp +d39 1 +a39 1 +bool findObject( const char* app, const char* obj, const char* func, QCStringList args ) +d121 1 +a121 1 + if ( types.count() != args.count() ) { +d131 1 +a131 1 + marshall(arg, args, i, *it); +d133 1 +a133 1 + if ( (uint) i != args.count() ) { +d224 1 +a224 5 + QCStringList params; + for( int i = 0; i < argc; i++ ) + params.append( args[ i ] ); + + findObject( app, objid, function, params ); +Index: client/marshall.cpp +=================================================================== +RCS file: /home/kde/kdelibs/dcop/client/marshall.cpp,v +retrieving revision 1.3 +diff -n -r1.3 marshall.cpp +d245 1 +a245 1 +void marshall( QDataStream &arg, QCStringList args, uint &i, QString type ) +d247 71 +a317 10 + if (type == "QStringList") + type = "QValueList"; + if (type == "QCStringList") + type = "QValueList"; + if( i > args.count() ) + { + qWarning("Not enough arguments."); + exit(1); + } + QString s = QString::fromLocal8Bit( args[ i ] ); +d319 28 +a346 57 + if ( type == "int" ) + arg << s.toInt(); + else if ( type == "uint" ) + arg << s.toUInt(); + else if ( type == "unsigned" ) + arg << s.toUInt(); + else if ( type == "unsigned int" ) + arg << s.toUInt(); + else if ( type == "long" ) + arg << s.toLong(); + else if ( type == "long int" ) + arg << s.toLong(); + else if ( type == "unsigned long" ) + arg << s.toULong(); + else if ( type == "unsigned long int" ) + arg << s.toULong(); + else if ( type == "float" ) + arg << s.toFloat(); + else if ( type == "double" ) + arg << s.toDouble(); + else if ( type == "bool" ) + arg << mkBool( s ); + else if ( type == "QString" ) + arg << s; + else if ( type == "QCString" ) + arg << QCString( args[ i ] ); + else if ( type == "QColor" ) + arg << mkColor( s ); + else if ( type == "QPoint" ) + arg << mkPoint( s ); + else if ( type == "QSize" ) + arg << mkSize( s ); + else if ( type == "QRect" ) + arg << mkRect( s ); + else if ( type == "QVariant" ) { + if ( s == "true" || s == "false" ) + arg << QVariant( mkBool( s ), 42 ); + else if ( s.left( 4 ) == "int(" ) + arg << QVariant( s.mid(4, s.length()-5).toInt() ); + else if ( s.left( 7 ) == "QPoint(" ) + arg << QVariant( mkPoint( s.mid(7, s.length()-8) ) ); + else if ( s.left( 6 ) == "QSize(" ) + arg << QVariant( mkSize( s.mid(6, s.length()-7) ) ); + else if ( s.left( 6 ) == "QRect(" ) + arg << QVariant( mkRect( s.mid(6, s.length()-7) ) ); + else if ( s.left( 7 ) == "QColor(" ) + arg << QVariant( mkColor( s.mid(7, s.length()-8) ) ); + else + arg << QVariant( s ); + } else if ( type.startsWith("QValueList<")) { + type = type.mid(11, type.length() - 12); + QStringList list; + QString delim = s; + if (delim == "[") + delim = "]"; + if (delim == "(") + delim = ")"; +a347 34 + QByteArray dummy_data; + QDataStream dummy_arg(dummy_data, IO_WriteOnly); + + uint j = i; + uint count = 0; + // Parse list to get the count + while (true) { + if( j > args.count() ) + { + qWarning("List end-delimiter '%s' not found.", delim.latin1()); + exit(1); + } + if( QString::fromLocal8Bit( args[ j ] ) == delim ) + break; + marshall( dummy_arg, args, j, type ); + count++; + } + arg << (Q_UINT32) count; + // Parse the list for real + while (true) { + if( i > args.count() ) + { + qWarning("List end-delimiter '%s' not found.", delim.latin1()); + exit(1); + } + if( QString::fromLocal8Bit( args[ i ] ) == delim ) + break; + marshall( arg, args, i, type ); + } + } else { + qWarning( "cannot handle datatype '%s'", type.latin1() ); + exit(1); + } + i++; diff --git a/kompare/tests/cvsdiff/unified.diff b/kompare/tests/cvsdiff/unified.diff new file mode 100644 index 00000000..562dee43 --- /dev/null +++ b/kompare/tests/cvsdiff/unified.diff @@ -0,0 +1,50 @@ +Index: client/dcopfind.cpp +=================================================================== +RCS file: /home/kde/kdelibs/dcop/client/dcopfind.cpp,v +retrieving revision 1.2 +diff -u -r1.2 dcopfind.cpp +--- client/dcopfind.cpp 2001/10/31 01:17:39 1.2 ++++ client/dcopfind.cpp 2002/01/16 18:07:51 +@@ -36,7 +36,7 @@ + static bool bAppIdOnly = 0; + static bool bLaunchApp = 0; + +-bool findObject( const char* app, const char* obj, const char* func, int argc, char** args ) ++bool findObject( const char* app, const char* obj, const char* func, QCStringList args ) + { + QString f = func; // Qt is better with unicode strings, so use one. + int left = f.find( '(' ); +@@ -118,7 +118,7 @@ + f = fc; + } + +- if ( (int) types.count() != argc ) { ++ if ( types.count() != args.count() ) { + qWarning( "arguments do not match" ); + exit(1); + } +@@ -128,9 +128,9 @@ + + int i = 0; + for ( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) { +- marshall(arg, argc, args, i, *it); ++ marshall(arg, args, i, *it); + } +- if ( (int) i != argc ) { ++ if ( (uint) i != args.count() ) { + qWarning( "arguments do not match" ); + exit(1); + } +@@ -221,7 +221,11 @@ + argc = 0; + } + +- findObject( app, objid, function, argc, args ); ++ QCStringList params; ++ for( int i = 0; i < argc; i++ ) ++ params.append( args[ i ] ); ++ ++ findObject( app, objid, function, params ); + + return 0; + } diff --git a/kompare/tests/cvsdiff/unifiedm.diff b/kompare/tests/cvsdiff/unifiedm.diff new file mode 100644 index 00000000..1de79f8f --- /dev/null +++ b/kompare/tests/cvsdiff/unifiedm.diff @@ -0,0 +1,924 @@ +Index: client/dcop.cpp +=================================================================== +RCS file: /home/kde/kdelibs/dcop/client/dcop.cpp,v +retrieving revision 1.26 +diff -u -r1.26 dcop.cpp +--- client/dcop.cpp 2001/10/31 01:17:39 1.26 ++++ client/dcop.cpp 2002/01/16 18:06:14 +@@ -20,19 +20,47 @@ + + ******************************************************************/ + +-#include ++#include ++#include ++#include ++ + #include +-#include "../kdatastream.h" ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++// putenv() is not available on all platforms, so make sure the emulation ++// wrapper is available in those cases by loading config.h! ++#include ++ + #include "../dcopclient.h" + #include "../dcopref.h" +-#include +-#include +-#include ++#include "../kdatastream.h" + + #include "marshall.cpp" + ++typedef QMap UserList; ++ + static DCOPClient* dcop = 0; + ++static QTextStream cout( stdout, IO_WriteOnly ); ++static QTextStream cerr( stderr, IO_WriteOnly ); ++ ++/** ++ * Session to send call to ++ * DefaultSession - current session. Current KDE session when called without ++ * --user or --all-users option. Otherwise this value ignores ++ * all users with more than one active session. ++ * AllSessions - Send to all sessions found. requires --user or --all-users. ++ * QuerySessions - Don't call DCOP, return a list of available sessions. ++ * CustomSession - Use the specified session ++ */ ++enum Session { DefaultSession = 0, AllSessions, QuerySessions, CustomSession }; ++ + bool startsWith(const QCString &id, const char *str, int n) + { + return !n || (strncmp(id.data(), str, n) == 0); +@@ -118,9 +146,8 @@ + } + } + +-void callFunction( const char* app, const char* obj, const char* func, int argc, char** args ) ++void callFunction( const char* app, const char* obj, const char* func, const QCStringList args ) + { +- + QString f = func; // Qt is better with unicode strings, so use one. + int left = f.find( '(' ); + int right = f.find( ')' ); +@@ -136,7 +163,7 @@ + bool ok = false; + QCStringList funcs = dcop->remoteFunctions( app, obj, &ok ); + QCString realfunc; +- if ( !ok && argc == 0 ) ++ if ( !ok && args.isEmpty() ) + goto doit; + if ( !ok ) + { +@@ -153,15 +180,16 @@ + + if ( l > 0 && (*it).mid( s, l - s ) == func ) { + realfunc = (*it).mid( s ); +- int a = (*it).contains(','); +- if ( ( a == 0 && argc == 0) || ( a > 0 && a + 1 == argc ) ) ++ uint a = (*it).contains(','); ++ if ( ( a == 0 && args.isEmpty() ) || ( a > 0 && a + 1 == args.count() ) ) + break; + } + } + if ( realfunc.isEmpty() ) + { + qWarning("no such function"); +- exit(1); ++// exit(1); ++ return; + } + f = realfunc; + left = f.find( '(' ); +@@ -243,11 +271,12 @@ + QCString replyType; + QDataStream arg(data, IO_WriteOnly); + +- int i = 0; +- for ( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) { +- marshall(arg, argc, args, i, *it); +- } +- if ( i != argc ) { ++ uint i = 0; ++ for( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) ++ marshall( arg, args, i, *it ); ++ ++ if ( i != args.count() ) ++ { + qWarning( "arguments do not match" ); + exit(1); + } +@@ -265,79 +294,480 @@ + } + } + } +- + ++/** ++ * Show command-line help and exit ++ */ ++void showHelp( int exitCode = 0 ) ++{ ++ cout << "Usage: dcop [options] [application [object [function [arg1] [arg2] ... ] ] ]" << endl ++ << "" << endl ++ << "Console DCOP client" << endl ++ << "" << endl ++ << "Generic options:" << endl ++ << " --help Show help about options" << endl ++ << "" << endl ++ << "Options:" << endl ++ << " --pipe Call DCOP for each line read from stdin" << endl ++ << " --user Connect to the given user's DCOP server. This option will" << endl ++ << " ignore the values of the environment vars $DCOPSERVER and" << endl ++ << " $ICEAUTHORITY, even if they are set." << endl ++ << " If the user has more than one open session, you must also" << endl ++ << " use one of the --list-sessions, --session or --als-sessions" << endl ++ << " command-line options." << endl ++ << " --all-users Send the same DCOP call to all users with a running DCOP" << endl ++ << " server. Only failed calls to existing DCOP servers will" ++ << " generate an error message. If no DCOP server is available" << endl ++ << " at all, no error will be generated." << endl; ++ ++ exit( exitCode ); ++} + +-int main( int argc, char** argv ) ++/** ++ * Return a list of all users and their home directories. ++ * Returns an empty list if /etc/passwd cannot be read for some reason. ++ */ ++static UserList userList() + { ++ UserList result; ++ ++ QFile f( "/etc/passwd" ); ++ ++ if( !f.open( IO_ReadOnly ) ) ++ { ++ cerr << "Can't open /etc/passwd for reading!" << endl; ++ return result; ++ } + +- if ( argc > 1 && argv[1][0] == '-' ) { +- fprintf( stderr, "Usage: dcop [ application [object [function [arg1] [arg2] [arg3] ... ] ] ] \n" ); +- exit(0); ++ QStringList l( QStringList::split( '\n', f.readAll() ) ); ++ ++ for( QStringList::ConstIterator it( l.begin() ); it != l.end(); ++it ) ++ { ++ QStringList userInfo( QStringList::split( ':', *it, true ) ); ++ result[ userInfo[ 0 ] ] = userInfo[ 5 ]; + } + +- DCOPClient client; +- client.attach(); +- dcop = &client; ++ return result; ++} ++ ++/** ++ * Return a list of available DCOP sessions for the specified user ++ * An empty list means no sessions are available, or an error occurred. ++ */ ++QStringList dcopSessionList( const QString &user, const QString &home ) ++{ ++ if( home.isEmpty() ) ++ { ++ cerr << "WARNING: Cannot determine home directory for user " ++ << user << "!" << endl ++ << "Please check permissions or set the $DCOPSERVER variable manually before" << endl ++ << "calling dcop." << endl; ++ return QStringList(); ++ } ++ ++ QStringList result; ++ QFileInfo dirInfo( home ); ++ if( !dirInfo.exists() || !dirInfo.isReadable() ) ++ return result; ++ ++ QDir d( home ); ++ d.setFilter( QDir::Files | QDir::Hidden | QDir::NoSymLinks ); ++ d.setNameFilter( ".DCOPserver*" ); ++ ++ const QFileInfoList *list = d.entryInfoList(); ++ if( !list ) ++ return result; ++ ++ QFileInfoListIterator it( *list ); ++ QFileInfo *fi; ++ ++ while ( ( fi = it.current() ) != 0 ) ++ { ++ if( fi->isReadable() ) ++ result.append( fi->fileName() ); ++ ++it; ++ } ++ return result; ++} + ++/** ++ * Do the actual DCOP call ++ */ ++void runDCOP( QCStringList args, UserList users, Session session, ++ const QString sessionName, bool readStdin ) ++{ + QCString app; + QCString objid; + QCString function; +- char **args = 0; +- if ((argc > 1) && (strncmp(argv[1], "DCOPRef(", 8)) == 0) ++ QCStringList params; ++ DCOPClient *client = 0L; ++ if ( !args.isEmpty() && args[ 0 ].find( "DCOPRef(" ) == 0 ) + { +- char *delim = strchr(argv[1], ','); +- if (!delim) +- { +- fprintf(stderr, "Error: '%s' is not a valid DCOP reference.\n", argv[1]); +- return 1; +- } +- *delim = 0; +- app = argv[1] + 8; +- delim++; +- delim[strlen(delim)-1] = 0; +- objid = delim; +- if (argc > 2) +- function = argv[2]; +- if (argc > 3) +- args = &argv[3]; +- argc++; ++ // WARNING: This part (until the closing '}') could very ++ // well be broken now. As I don't know how to trigger and test ++ // dcoprefs this code is *not* tested. It compiles and it looks ++ // ok to me, but that's all I can say - Martijn (2001/12/24) ++ int delimPos = args[ 0 ].findRev( ',' ); ++ if( delimPos == -1 ) ++ { ++ cerr << "Error: '" << args[ 0 ] ++ << "' is not a valid DCOP reference." << endl; ++ exit( -1 ); ++ } ++ args[ 0 ][ delimPos ] = 0; ++ app = args[ 0 ].mid( 8 ); ++ delimPos++; ++ args[ 0 ][ args[ 0 ].length() - 1 ] = 0; ++ objid = args[ 0 ].mid( delimPos ); ++ if( args.count() > 1 ) ++ function = args[ 1 ]; ++ if( args.count() > 2 ) ++ { ++ params = args; ++ params.remove( params.begin() ); ++ params.remove( params.begin() ); ++ } + } + else + { +- if (argc > 1) +- app = argv[1]; +- if (argc > 2) +- objid = argv[2]; +- if (argc > 3) +- function = argv[3]; +- if (argc > 4) +- args = &argv[4]; +- } +- +- switch ( argc ) { +- case 0: +- case 1: +- queryApplications(""); +- break; +- case 2: +- if (endsWith(app, '*')) +- queryApplications(app); +- else +- queryObjects( app, "" ); +- break; +- case 3: +- if (endsWith(objid, '*')) +- queryObjects(app, objid); +- else +- queryFunctions( app, objid ); +- break; +- case 4: +- default: +- callFunction( app, objid, function, argc - 4, args ); +- break; ++ if( !args.isEmpty() ) ++ app = args[ 0 ]; ++ if( args.count() > 1 ) ++ objid = args[ 1 ]; ++ if( args.count() > 2 ) ++ function = args[ 2 ]; ++ if( args.count() > 3) ++ { ++ params = args; ++ params.remove( params.begin() ); ++ params.remove( params.begin() ); ++ params.remove( params.begin() ); ++ } ++ } ++ ++ bool firstRun = true; ++ UserList::Iterator it; ++ QStringList sessions; ++ bool presetDCOPServer = false; ++// char *dcopStr = 0L; ++ QString dcopServer; ++ ++ for( it = users.begin(); it != users.end() || firstRun; it++ ) ++ { ++ firstRun = false; ++ ++ //cout << "Iterating '" << it.key() << "'" << endl; ++ ++ if( session == QuerySessions ) ++ { ++ QStringList sessions = dcopSessionList( it.key(), it.data() ); ++ if( sessions.isEmpty() ) ++ { ++ cout << "No active sessions"; ++ if( !( *it ).isEmpty() ) ++ cout << " for user " << *it; ++ cout << endl; ++ } ++ else ++ { ++ cout << "Active sessions "; ++ if( !( *it ).isEmpty() ) ++ cout << "for user " << *it << " "; ++ cout << ":" << endl; ++ ++ QStringList::Iterator sIt; ++ for( sIt = sessions.begin(); sIt != sessions.end(); sIt++ ) ++ cout << " " << *sIt << endl; ++ ++ cout << endl; ++ } ++ continue; ++ } ++ ++ if( getenv( "DCOPSERVER" ) ) ++ { ++ sessions.append( getenv( "DCOPSERVER" ) ); ++ presetDCOPServer = true; ++ } ++ ++ if( users.count() > 1 || ( users.count() == 1 && ++ ( getenv( "DCOPSERVER" ) == 0 /*&& getenv( "DISPLAY" ) == 0*/ ) ) ) ++ { ++ sessions = dcopSessionList( it.key(), it.data() ); ++ if( sessions.isEmpty() ) ++ { ++ if( users.count() > 1 ) ++ continue; ++ else ++ { ++ cerr << "ERROR: No active KDE sessions!" << endl ++ << "If you are sure there is one, please set the $DCOPSERVER variable manually" << endl ++ << "before calling dcop." << endl; ++ exit( -1 ); ++ } ++ } ++ else if( sessions.count() > 1 && session != AllSessions ) ++ { ++ cerr << "ERROR: Multiple available KDE sessions!" << endl ++ << "Please specify the correct session to use with --session or use the" << endl ++ << "--all-sessions option to broadcast to all sessions." << endl; ++ exit( -1 ); ++ } ++ } + ++ if( users.count() > 1 || ( users.count() == 1 && ++ ( getenv( "ICEAUTHORITY" ) == 0 || getenv( "DISPLAY" ) == 0 ) ) ) ++ { ++ // Check for ICE authority file and if the file can be read by us ++ QString home = it.data(); ++ QString iceFile = it.data() + "/.ICEauthority"; ++ QFileInfo fi( iceFile ); ++ if( iceFile.isEmpty() ) ++ { ++ cerr << "WARNING: Cannot determine home directory for user " ++ << it.key() << "!" << endl ++ << "Please check permissions or set the $ICEAUTHORITY variable manually before" << endl ++ << "calling dcop." << endl; ++ } ++ else if( fi.exists() ) ++ { ++ if( fi.isReadable() ) ++ { ++ char *envStr = strdup( ( "ICEAUTHORITY=" + iceFile ).ascii() ); ++ putenv( envStr ); ++ //cerr << "ice: " << envStr << endl; ++ } ++ else ++ { ++ cerr << "WARNING: ICE authority file " << iceFile ++ << "is not readable by you!" << endl ++ << "Please check permissions or set the $ICEAUTHORITY variable manually before" << endl ++ << "calling dcop." << endl; ++ } ++ } ++ else ++ { ++ if( users.count() > 1 ) ++ continue; ++ else ++ { ++ cerr << "WARNING: Cannot find ICE authority file " ++ << iceFile << "!" << endl ++ << "Please check permissions or set the $ICEAUTHORITY" ++ << " variable manually before" << endl ++ << "calling dcop." << endl; ++ } ++ } ++ } ++ ++ // Main loop ++ // If users is an empty list we're calling for the currently logged ++ // in user. In this case we don't have a session, but still want ++ // to iterate the loop once. ++ QStringList::Iterator sIt = sessions.begin(); ++ for( ; sIt != sessions.end() || users.isEmpty(); sIt++ ) ++ { ++ if( !presetDCOPServer && !users.isEmpty() ) ++ { ++ QString dcopFile = it.data() + "/" + *sIt; ++ QFile f( dcopFile ); ++ if( !f.open( IO_ReadOnly ) ) ++ { ++ cerr << "Can't open " << dcopFile << " for reading!" << endl; ++ exit( -1 ); ++ } ++ ++ QStringList l( QStringList::split( '\n', f.readAll() ) ); ++ dcopServer = l.first(); ++ ++ if( dcopServer.isEmpty() ) ++ { ++ cerr << "WARNING: Unable to determine DCOP server for session " ++ << *sIt << "!" << endl ++ << "Please check permissions or set the $DCOPSERVER variable manually before" << endl ++ << "calling dcop." << endl; ++ exit( -1 ); ++ } ++ } ++ ++ delete client; ++ client = new DCOPClient; ++ if( !dcopServer.isEmpty() ) ++ client->setServerAddress( dcopServer.ascii() ); ++ bool success = client->attach(); ++ if( !success ) ++ { ++ cerr << "ERROR: Couldn't attach to DCOP server!" << endl; ++ continue; ++ } ++ dcop = client; ++ ++ switch ( args.count() ) ++ { ++ case 0: ++ queryApplications(""); ++ break; ++ case 1: ++ if (endsWith(app, '*')) ++ queryApplications(app); ++ else ++ queryObjects( app, "" ); ++ break; ++ case 2: ++ if (endsWith(objid, '*')) ++ queryObjects(app, objid); ++ else ++ queryFunctions( app, objid ); ++ break; ++ case 3: ++ default: ++ if( readStdin ) ++ { ++ QCStringList::Iterator replaceArg = args.end(); ++ ++ QCStringList::Iterator it; ++ for( it = args.begin(); it != args.end(); it++ ) ++ if( *it == "%1" ) ++ replaceArg = it; ++ ++ // Read from stdin until EOF and call function for each line read ++ char *buf = new char[ 1000 ]; ++ while ( !feof( stdin ) ) ++ { ++ fgets( buf, 1000, stdin ); ++ ++ if( replaceArg != args.end() ) ++ *replaceArg = buf; ++ ++ callFunction( app, objid, function, params ); ++ } ++ } ++ else ++ { ++ // Just call function ++// cout << "call " << app << ", " << objid << ", " << function << ", (params)" << endl; ++ callFunction( app, objid, function, params ); ++ } ++ break; ++ } ++ // Another sIt++ would make the loop infinite... ++ if( users.isEmpty() ) ++ break; ++ } ++ ++ // Another it++ would make the loop infinite... ++ if( it == users.end() ) ++ break; + } ++} ++ + ++int main( int argc, char** argv ) ++{ ++ bool readStdin = false; ++ int numOptions = 0; ++ QString user; ++ Session session = DefaultSession; ++ QString sessionName; ++ ++ // Scan for command-line options first ++ for( int pos = 1 ; pos <= argc - 1 ; pos++ ) ++ { ++ if( strcmp( argv[ pos ], "--help" ) == 0 ) ++ showHelp( 0 ); ++ else if( strcmp( argv[ pos ], "--pipe" ) == 0 ) ++ { ++ readStdin = true; ++ numOptions++; ++ } ++ else if( strcmp( argv[ pos ], "--user" ) == 0 ) ++ { ++ if( pos <= argc - 2 ) ++ { ++ user = QString::fromLocal8Bit( argv[ pos + 1] ); ++ numOptions +=2; ++ pos++; ++ } ++ else ++ { ++ cerr << "Missing username for '--user' option!" << endl << endl; ++ showHelp( -1 ); ++ } ++ } ++ else if( strcmp( argv[ pos ], "--all-users" ) == 0 ) ++ { ++ user = "*"; ++ numOptions ++; ++ } ++ else if( strcmp( argv[ pos ], "--list-sessions" ) == 0 ) ++ { ++ session = QuerySessions; ++ numOptions ++; ++ } ++ else if( strcmp( argv[ pos ], "--all-sessions" ) == 0 ) ++ { ++ session = AllSessions; ++ numOptions ++; ++ } ++ else if( argv[ pos ][ 0 ] == '-' ) ++ { ++ cerr << "Unknown command-line option '" << argv[ pos ] ++ << "'." << endl << endl; ++ showHelp( -1 ); ++ } ++ else ++ break; // End of options ++ } ++ ++ argc -= numOptions; ++ ++ QCStringList args; ++ for( int i = numOptions; i < argc + numOptions - 1; i++ ) ++ args.append( argv[ i + 1 ] ); ++ ++ if( readStdin && args.count() < 3 ) ++ { ++ cerr << "--pipe option only supported for function calls!" << endl << endl; ++ showHelp( -1 ); ++ } ++ ++ if( user == "*" && args.count() < 3 && session != QuerySessions ) ++ { ++ cerr << "ERROR: The --all-users option is only supported for function calls!" << endl << endl; ++ showHelp( -1 ); ++ } ++ ++ if( session == QuerySessions && !args.isEmpty() ) ++ { ++ cerr << "ERROR: The --list-sessions option cannot be used for actual DCOP calls!" << endl << endl; ++ showHelp( -1 ); ++ } ++ ++ if( session == QuerySessions && user.isEmpty() ) ++ { ++ cerr << "ERROR: The --list-sessions option can only be used with the --user or" << endl ++ << "--all-users options!" << endl << endl; ++ showHelp( -1 ); ++ } ++ ++ if( session != DefaultSession && session != QuerySessions && ++ args.count() < 3 ) ++ { ++ cerr << "ERROR: The --session and --all-sessions options are only supported for function" << endl ++ << "calls!" << endl << endl; ++ showHelp( -1 ); ++ } ++ ++ UserList users; ++ if( user == "*" ) ++ users = userList(); ++ else if( !user.isEmpty() ) ++ users[ user ] = userList()[ user ]; ++ ++ runDCOP( args, users, session, sessionName, readStdin ); ++ + return 0; + } ++ ++// vim: set ts=8 sts=4 sw=4 noet: ++ +Index: client/dcopfind.cpp +=================================================================== +RCS file: /home/kde/kdelibs/dcop/client/dcopfind.cpp,v +retrieving revision 1.2 +diff -u -r1.2 dcopfind.cpp +--- client/dcopfind.cpp 2001/10/31 01:17:39 1.2 ++++ client/dcopfind.cpp 2002/01/16 18:06:14 +@@ -36,7 +36,7 @@ + static bool bAppIdOnly = 0; + static bool bLaunchApp = 0; + +-bool findObject( const char* app, const char* obj, const char* func, int argc, char** args ) ++bool findObject( const char* app, const char* obj, const char* func, QCStringList args ) + { + QString f = func; // Qt is better with unicode strings, so use one. + int left = f.find( '(' ); +@@ -118,7 +118,7 @@ + f = fc; + } + +- if ( (int) types.count() != argc ) { ++ if ( types.count() != args.count() ) { + qWarning( "arguments do not match" ); + exit(1); + } +@@ -128,9 +128,9 @@ + + int i = 0; + for ( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) { +- marshall(arg, argc, args, i, *it); ++ marshall(arg, args, i, *it); + } +- if ( (int) i != argc ) { ++ if ( (uint) i != args.count() ) { + qWarning( "arguments do not match" ); + exit(1); + } +@@ -221,7 +221,11 @@ + argc = 0; + } + +- findObject( app, objid, function, argc, args ); ++ QCStringList params; ++ for( int i = 0; i < argc; i++ ) ++ params.append( args[ i ] ); ++ ++ findObject( app, objid, function, params ); + + return 0; + } +Index: client/marshall.cpp +=================================================================== +RCS file: /home/kde/kdelibs/dcop/client/marshall.cpp,v +retrieving revision 1.3 +diff -u -r1.3 marshall.cpp +--- client/marshall.cpp 2001/10/31 01:17:39 1.3 ++++ client/marshall.cpp 2002/01/16 18:06:14 +@@ -242,108 +242,110 @@ + + } + +-void marshall(QDataStream &arg, int argc, char **argv, int &i, QString type) ++void marshall( QDataStream &arg, QCStringList args, uint &i, QString type ) + { +- if (type == "QStringList") +- type = "QValueList"; +- if (type == "QCStringList") +- type = "QValueList"; +- if (i >= argc) +- { +- qWarning("Not enough arguments."); +- exit(1); +- } +- QString s = QString::fromLocal8Bit(argv[i]); +- +- if ( type == "int" ) +- arg << s.toInt(); +- else if ( type == "uint" ) +- arg << s.toUInt(); +- else if ( type == "unsigned" ) +- arg << s.toUInt(); +- else if ( type == "unsigned int" ) +- arg << s.toUInt(); +- else if ( type == "long" ) +- arg << s.toLong(); +- else if ( type == "long int" ) +- arg << s.toLong(); +- else if ( type == "unsigned long" ) +- arg << s.toULong(); +- else if ( type == "unsigned long int" ) +- arg << s.toULong(); +- else if ( type == "float" ) +- arg << s.toFloat(); +- else if ( type == "double" ) +- arg << s.toDouble(); +- else if ( type == "bool" ) +- arg << mkBool( s ); +- else if ( type == "QString" ) +- arg << s; +- else if ( type == "QCString" ) +- arg << QCString( argv[i] ); +- else if ( type == "QColor" ) +- arg << mkColor( s ); +- else if ( type == "QPoint" ) +- arg << mkPoint( s ); +- else if ( type == "QSize" ) +- arg << mkSize( s ); +- else if ( type == "QRect" ) +- arg << mkRect( s ); +- else if ( type == "QVariant" ) { +- if ( s == "true" || s == "false" ) +- arg << QVariant( mkBool( s ), 42 ); +- else if ( s.left( 4 ) == "int(" ) +- arg << QVariant( s.mid(4, s.length()-5).toInt() ); +- else if ( s.left( 7 ) == "QPoint(" ) +- arg << QVariant( mkPoint( s.mid(7, s.length()-8) ) ); +- else if ( s.left( 6 ) == "QSize(" ) +- arg << QVariant( mkSize( s.mid(6, s.length()-7) ) ); +- else if ( s.left( 6 ) == "QRect(" ) +- arg << QVariant( mkRect( s.mid(6, s.length()-7) ) ); +- else if ( s.left( 7 ) == "QColor(" ) +- arg << QVariant( mkColor( s.mid(7, s.length()-8) ) ); +- else +- arg << QVariant( s ); +- } else if ( type.startsWith("QValueList<")) { +- type = type.mid(11, type.length() - 12); +- QStringList list; +- QString delim = s; +- if (delim == "[") +- delim = "]"; +- if (delim == "(") +- delim = ")"; +- i++; +- QByteArray dummy_data; +- QDataStream dummy_arg(dummy_data, IO_WriteOnly); ++ if (type == "QStringList") ++ type = "QValueList"; ++ if (type == "QCStringList") ++ type = "QValueList"; ++ if( i > args.count() ) ++ { ++ qWarning("Not enough arguments."); ++ exit(1); ++ } ++ QString s = QString::fromLocal8Bit( args[ i ] ); + +- int j = i; +- int count = 0; +- // Parse list to get the count +- while (true) { +- if (j >= argc) +- { +- qWarning("List end-delimiter '%s' not found.", delim.latin1()); +- exit(1); +- } +- if (argv[j] == delim) break; +- marshall(dummy_arg, argc, argv, j, type); +- count++; +- } +- arg << (Q_UINT32) count; +- // Parse the list for real +- while (true) { +- if (i >= argc) +- { +- qWarning("List end-delimiter '%s' not found.", delim.latin1()); +- exit(1); +- } +- if (argv[i] == delim) break; +- marshall(arg, argc, argv, i, type); +- } +- } else { +- qWarning( "cannot handle datatype '%s'", type.latin1() ); +- exit(1); +- } ++ if ( type == "int" ) ++ arg << s.toInt(); ++ else if ( type == "uint" ) ++ arg << s.toUInt(); ++ else if ( type == "unsigned" ) ++ arg << s.toUInt(); ++ else if ( type == "unsigned int" ) ++ arg << s.toUInt(); ++ else if ( type == "long" ) ++ arg << s.toLong(); ++ else if ( type == "long int" ) ++ arg << s.toLong(); ++ else if ( type == "unsigned long" ) ++ arg << s.toULong(); ++ else if ( type == "unsigned long int" ) ++ arg << s.toULong(); ++ else if ( type == "float" ) ++ arg << s.toFloat(); ++ else if ( type == "double" ) ++ arg << s.toDouble(); ++ else if ( type == "bool" ) ++ arg << mkBool( s ); ++ else if ( type == "QString" ) ++ arg << s; ++ else if ( type == "QCString" ) ++ arg << QCString( args[ i ] ); ++ else if ( type == "QColor" ) ++ arg << mkColor( s ); ++ else if ( type == "QPoint" ) ++ arg << mkPoint( s ); ++ else if ( type == "QSize" ) ++ arg << mkSize( s ); ++ else if ( type == "QRect" ) ++ arg << mkRect( s ); ++ else if ( type == "QVariant" ) { ++ if ( s == "true" || s == "false" ) ++ arg << QVariant( mkBool( s ), 42 ); ++ else if ( s.left( 4 ) == "int(" ) ++ arg << QVariant( s.mid(4, s.length()-5).toInt() ); ++ else if ( s.left( 7 ) == "QPoint(" ) ++ arg << QVariant( mkPoint( s.mid(7, s.length()-8) ) ); ++ else if ( s.left( 6 ) == "QSize(" ) ++ arg << QVariant( mkSize( s.mid(6, s.length()-7) ) ); ++ else if ( s.left( 6 ) == "QRect(" ) ++ arg << QVariant( mkRect( s.mid(6, s.length()-7) ) ); ++ else if ( s.left( 7 ) == "QColor(" ) ++ arg << QVariant( mkColor( s.mid(7, s.length()-8) ) ); ++ else ++ arg << QVariant( s ); ++ } else if ( type.startsWith("QValueList<")) { ++ type = type.mid(11, type.length() - 12); ++ QStringList list; ++ QString delim = s; ++ if (delim == "[") ++ delim = "]"; ++ if (delim == "(") ++ delim = ")"; + i++; ++ QByteArray dummy_data; ++ QDataStream dummy_arg(dummy_data, IO_WriteOnly); ++ ++ uint j = i; ++ uint count = 0; ++ // Parse list to get the count ++ while (true) { ++ if( j > args.count() ) ++ { ++ qWarning("List end-delimiter '%s' not found.", delim.latin1()); ++ exit(1); ++ } ++ if( QString::fromLocal8Bit( args[ j ] ) == delim ) ++ break; ++ marshall( dummy_arg, args, j, type ); ++ count++; ++ } ++ arg << (Q_UINT32) count; ++ // Parse the list for real ++ while (true) { ++ if( i > args.count() ) ++ { ++ qWarning("List end-delimiter '%s' not found.", delim.latin1()); ++ exit(1); ++ } ++ if( QString::fromLocal8Bit( args[ i ] ) == delim ) ++ break; ++ marshall( arg, args, i, type ); ++ } ++ } else { ++ qWarning( "cannot handle datatype '%s'", type.latin1() ); ++ exit(1); ++ } ++ i++; + } + diff --git a/kompare/tests/diff/context.diff b/kompare/tests/diff/context.diff new file mode 100644 index 00000000..e17df000 --- /dev/null +++ b/kompare/tests/diff/context.diff @@ -0,0 +1,27 @@ +*** /home/John/lao Thu Apr 12 11:09:30 2001 +--- /home/John/tzu Sat Jul 28 13:23:25 2001 +*************** +*** 1,7 **** +- The Way that can be told of is not the eternal Way; +- The name that can be named is not the eternal name. + The Nameless is the origin of Heaven and Earth; +! The Named is the mother of all things. + Therefore let there always be non-being, + so we may see their subtlety, + And let there always be being, +--- 1,6 ---- + The Nameless is the origin of Heaven and Earth; +! The named is the mother of all things. +! + Therefore let there always be non-being, + so we may see their subtlety, + And let there always be being, +*************** +*** 9,11 **** +--- 8,13 ---- + The two are the same, + But after they are produced, + they have different names. ++ They both may be called deep and profound. ++ Deeper and more profound, ++ The door of all subtleties! diff --git a/kompare/tests/diff/contextm.diff b/kompare/tests/diff/contextm.diff new file mode 100644 index 00000000..0b1bba5a --- /dev/null +++ b/kompare/tests/diff/contextm.diff @@ -0,0 +1,1032 @@ +diff -cr dcop/client/dcop.cpp dcop2/client/dcop.cpp +*** dcop/client/dcop.cpp Wed Jan 30 22:38:07 2002 +--- dcop2/client/dcop.cpp Wed Jan 30 22:37:04 2002 +*************** +*** 20,38 **** + + ******************************************************************/ + +! #include + #include +! #include "../kdatastream.h" + #include "../dcopclient.h" + #include "../dcopref.h" +! #include +! #include +! #include + + #include "marshall.cpp" + + static DCOPClient* dcop = 0; + + bool startsWith(const QCString &id, const char *str, int n) + { + return !n || (strncmp(id.data(), str, n) == 0); +--- 20,66 ---- + + ******************************************************************/ + +! #include +! #include +! #include +! + #include +! #include +! #include +! #include +! #include +! #include +! #include +! #include +! +! // putenv() is not available on all platforms, so make sure the emulation +! // wrapper is available in those cases by loading config.h! +! #include +! + #include "../dcopclient.h" + #include "../dcopref.h" +! #include "../kdatastream.h" + + #include "marshall.cpp" + ++ typedef QMap UserList; ++ + static DCOPClient* dcop = 0; + ++ static QTextStream cout( stdout, IO_WriteOnly ); ++ static QTextStream cerr( stderr, IO_WriteOnly ); ++ ++ /** ++ * Session to send call to ++ * DefaultSession - current session. Current KDE session when called without ++ * --user or --all-users option. Otherwise this value ignores ++ * all users with more than one active session. ++ * AllSessions - Send to all sessions found. requires --user or --all-users. ++ * QuerySessions - Don't call DCOP, return a list of available sessions. ++ * CustomSession - Use the specified session ++ */ ++ enum Session { DefaultSession = 0, AllSessions, QuerySessions, CustomSession }; ++ + bool startsWith(const QCString &id, const char *str, int n) + { + return !n || (strncmp(id.data(), str, n) == 0); +*************** +*** 118,126 **** + } + } + +! void callFunction( const char* app, const char* obj, const char* func, int argc, char** args ) + { +- + QString f = func; // Qt is better with unicode strings, so use one. + int left = f.find( '(' ); + int right = f.find( ')' ); +--- 146,153 ---- + } + } + +! void callFunction( const char* app, const char* obj, const char* func, const QCStringList args ) + { + QString f = func; // Qt is better with unicode strings, so use one. + int left = f.find( '(' ); + int right = f.find( ')' ); +*************** +*** 136,142 **** + bool ok = false; + QCStringList funcs = dcop->remoteFunctions( app, obj, &ok ); + QCString realfunc; +! if ( !ok && argc == 0 ) + goto doit; + if ( !ok ) + { +--- 163,169 ---- + bool ok = false; + QCStringList funcs = dcop->remoteFunctions( app, obj, &ok ); + QCString realfunc; +! if ( !ok && args.isEmpty() ) + goto doit; + if ( !ok ) + { +*************** +*** 153,167 **** + + if ( l > 0 && (*it).mid( s, l - s ) == func ) { + realfunc = (*it).mid( s ); +! int a = (*it).contains(','); +! if ( ( a == 0 && argc == 0) || ( a > 0 && a + 1 == argc ) ) + break; + } + } + if ( realfunc.isEmpty() ) + { + qWarning("no such function"); +! exit(1); + } + f = realfunc; + left = f.find( '(' ); +--- 180,195 ---- + + if ( l > 0 && (*it).mid( s, l - s ) == func ) { + realfunc = (*it).mid( s ); +! uint a = (*it).contains(','); +! if ( ( a == 0 && args.isEmpty() ) || ( a > 0 && a + 1 == args.count() ) ) + break; + } + } + if ( realfunc.isEmpty() ) + { + qWarning("no such function"); +! // exit(1); +! return; + } + f = realfunc; + left = f.find( '(' ); +*************** +*** 243,253 **** + QCString replyType; + QDataStream arg(data, IO_WriteOnly); + +! int i = 0; +! for ( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) { +! marshall(arg, argc, args, i, *it); +! } +! if ( i != argc ) { + qWarning( "arguments do not match" ); + exit(1); + } +--- 271,282 ---- + QCString replyType; + QDataStream arg(data, IO_WriteOnly); + +! uint i = 0; +! for( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) +! marshall( arg, args, i, *it ); +! +! if ( i != args.count() ) +! { + qWarning( "arguments do not match" ); + exit(1); + } +*************** +*** 266,343 **** + } + } + + +! +! int main( int argc, char** argv ) + { + +! if ( argc > 1 && argv[1][0] == '-' ) { +! fprintf( stderr, "Usage: dcop [ application [object [function [arg1] [arg2] [arg3] ... ] ] ] \n" ); +! exit(0); + } + +! DCOPClient client; +! client.attach(); +! dcop = &client; + + QCString app; + QCString objid; + QCString function; +! char **args = 0; +! if ((argc > 1) && (strncmp(argv[1], "DCOPRef(", 8)) == 0) + { +! char *delim = strchr(argv[1], ','); +! if (!delim) +! { +! fprintf(stderr, "Error: '%s' is not a valid DCOP reference.\n", argv[1]); +! return 1; +! } +! *delim = 0; +! app = argv[1] + 8; +! delim++; +! delim[strlen(delim)-1] = 0; +! objid = delim; +! if (argc > 2) +! function = argv[2]; +! if (argc > 3) +! args = &argv[3]; +! argc++; + } + else + { +! if (argc > 1) +! app = argv[1]; +! if (argc > 2) +! objid = argv[2]; +! if (argc > 3) +! function = argv[3]; +! if (argc > 4) +! args = &argv[4]; +! } +! +! switch ( argc ) { +! case 0: +! case 1: +! queryApplications(""); +! break; +! case 2: +! if (endsWith(app, '*')) +! queryApplications(app); +! else +! queryObjects( app, "" ); +! break; +! case 3: +! if (endsWith(objid, '*')) +! queryObjects(app, objid); +! else +! queryFunctions( app, objid ); +! break; +! case 4: +! default: +! callFunction( app, objid, function, argc - 4, args ); +! break; + + } + + return 0; + } +--- 295,773 ---- + } + } + ++ /** ++ * Show command-line help and exit ++ */ ++ void showHelp( int exitCode = 0 ) ++ { ++ cout << "Usage: dcop [options] [application [object [function [arg1] [arg2] ... ] ] ]" << endl ++ << "" << endl ++ << "Console DCOP client" << endl ++ << "" << endl ++ << "Generic options:" << endl ++ << " --help Show help about options" << endl ++ << "" << endl ++ << "Options:" << endl ++ << " --pipe Call DCOP for each line read from stdin" << endl ++ << " --user Connect to the given user's DCOP server. This option will" << endl ++ << " ignore the values of the environment vars $DCOPSERVER and" << endl ++ << " $ICEAUTHORITY, even if they are set." << endl ++ << " If the user has more than one open session, you must also" << endl ++ << " use one of the --list-sessions, --session or --als-sessions" << endl ++ << " command-line options." << endl ++ << " --all-users Send the same DCOP call to all users with a running DCOP" << endl ++ << " server. Only failed calls to existing DCOP servers will" ++ << " generate an error message. If no DCOP server is available" << endl ++ << " at all, no error will be generated." << endl; ++ ++ exit( exitCode ); ++ } + +! /** +! * Return a list of all users and their home directories. +! * Returns an empty list if /etc/passwd cannot be read for some reason. +! */ +! static UserList userList() + { ++ UserList result; ++ ++ QFile f( "/etc/passwd" ); ++ ++ if( !f.open( IO_ReadOnly ) ) ++ { ++ cerr << "Can't open /etc/passwd for reading!" << endl; ++ return result; ++ } + +! QStringList l( QStringList::split( '\n', f.readAll() ) ); +! +! for( QStringList::ConstIterator it( l.begin() ); it != l.end(); ++it ) +! { +! QStringList userInfo( QStringList::split( ':', *it, true ) ); +! result[ userInfo[ 0 ] ] = userInfo[ 5 ]; + } + +! return result; +! } +! +! /** +! * Return a list of available DCOP sessions for the specified user +! * An empty list means no sessions are available, or an error occurred. +! */ +! QStringList dcopSessionList( const QString &user, const QString &home ) +! { +! if( home.isEmpty() ) +! { +! cerr << "WARNING: Cannot determine home directory for user " +! << user << "!" << endl +! << "Please check permissions or set the $DCOPSERVER variable manually before" << endl +! << "calling dcop." << endl; +! return QStringList(); +! } +! +! QStringList result; +! QFileInfo dirInfo( home ); +! if( !dirInfo.exists() || !dirInfo.isReadable() ) +! return result; +! +! QDir d( home ); +! d.setFilter( QDir::Files | QDir::Hidden | QDir::NoSymLinks ); +! d.setNameFilter( ".DCOPserver*" ); +! +! const QFileInfoList *list = d.entryInfoList(); +! if( !list ) +! return result; +! +! QFileInfoListIterator it( *list ); +! QFileInfo *fi; +! +! while ( ( fi = it.current() ) != 0 ) +! { +! if( fi->isReadable() ) +! result.append( fi->fileName() ); +! ++it; +! } +! return result; +! } + ++ /** ++ * Do the actual DCOP call ++ */ ++ void runDCOP( QCStringList args, UserList users, Session session, ++ const QString sessionName, bool readStdin ) ++ { + QCString app; + QCString objid; + QCString function; +! QCStringList params; +! DCOPClient *client = 0L; +! if ( !args.isEmpty() && args[ 0 ].find( "DCOPRef(" ) == 0 ) + { +! // WARNING: This part (until the closing '}') could very +! // well be broken now. As I don't know how to trigger and test +! // dcoprefs this code is *not* tested. It compiles and it looks +! // ok to me, but that's all I can say - Martijn (2001/12/24) +! int delimPos = args[ 0 ].findRev( ',' ); +! if( delimPos == -1 ) +! { +! cerr << "Error: '" << args[ 0 ] +! << "' is not a valid DCOP reference." << endl; +! exit( -1 ); +! } +! args[ 0 ][ delimPos ] = 0; +! app = args[ 0 ].mid( 8 ); +! delimPos++; +! args[ 0 ][ args[ 0 ].length() - 1 ] = 0; +! objid = args[ 0 ].mid( delimPos ); +! if( args.count() > 1 ) +! function = args[ 1 ]; +! if( args.count() > 2 ) +! { +! params = args; +! params.remove( params.begin() ); +! params.remove( params.begin() ); +! } + } + else + { +! if( !args.isEmpty() ) +! app = args[ 0 ]; +! if( args.count() > 1 ) +! objid = args[ 1 ]; +! if( args.count() > 2 ) +! function = args[ 2 ]; +! if( args.count() > 3) +! { +! params = args; +! params.remove( params.begin() ); +! params.remove( params.begin() ); +! params.remove( params.begin() ); +! } +! } +! +! bool firstRun = true; +! UserList::Iterator it; +! QStringList sessions; +! bool presetDCOPServer = false; +! // char *dcopStr = 0L; +! QString dcopServer; +! +! for( it = users.begin(); it != users.end() || firstRun; it++ ) +! { +! firstRun = false; +! +! //cout << "Iterating '" << it.key() << "'" << endl; +! +! if( session == QuerySessions ) +! { +! QStringList sessions = dcopSessionList( it.key(), it.data() ); +! if( sessions.isEmpty() ) +! { +! cout << "No active sessions"; +! if( !( *it ).isEmpty() ) +! cout << " for user " << *it; +! cout << endl; +! } +! else +! { +! cout << "Active sessions "; +! if( !( *it ).isEmpty() ) +! cout << "for user " << *it << " "; +! cout << ":" << endl; +! +! QStringList::Iterator sIt; +! for( sIt = sessions.begin(); sIt != sessions.end(); sIt++ ) +! cout << " " << *sIt << endl; +! +! cout << endl; +! } +! continue; +! } +! +! if( getenv( "DCOPSERVER" ) ) +! { +! sessions.append( getenv( "DCOPSERVER" ) ); +! presetDCOPServer = true; +! } +! +! if( users.count() > 1 || ( users.count() == 1 && +! ( getenv( "DCOPSERVER" ) == 0 /*&& getenv( "DISPLAY" ) == 0*/ ) ) ) +! { +! sessions = dcopSessionList( it.key(), it.data() ); +! if( sessions.isEmpty() ) +! { +! if( users.count() > 1 ) +! continue; +! else +! { +! cerr << "ERROR: No active KDE sessions!" << endl +! << "If you are sure there is one, please set the $DCOPSERVER variable manually" << endl +! << "before calling dcop." << endl; +! exit( -1 ); +! } +! } +! else if( sessions.count() > 1 && session != AllSessions ) +! { +! cerr << "ERROR: Multiple available KDE sessions!" << endl +! << "Please specify the correct session to use with --session or use the" << endl +! << "--all-sessions option to broadcast to all sessions." << endl; +! exit( -1 ); +! } +! } + ++ if( users.count() > 1 || ( users.count() == 1 && ++ ( getenv( "ICEAUTHORITY" ) == 0 || getenv( "DISPLAY" ) == 0 ) ) ) ++ { ++ // Check for ICE authority file and if the file can be read by us ++ QString home = it.data(); ++ QString iceFile = it.data() + "/.ICEauthority"; ++ QFileInfo fi( iceFile ); ++ if( iceFile.isEmpty() ) ++ { ++ cerr << "WARNING: Cannot determine home directory for user " ++ << it.key() << "!" << endl ++ << "Please check permissions or set the $ICEAUTHORITY variable manually before" << endl ++ << "calling dcop." << endl; ++ } ++ else if( fi.exists() ) ++ { ++ if( fi.isReadable() ) ++ { ++ char *envStr = strdup( ( "ICEAUTHORITY=" + iceFile ).ascii() ); ++ putenv( envStr ); ++ //cerr << "ice: " << envStr << endl; ++ } ++ else ++ { ++ cerr << "WARNING: ICE authority file " << iceFile ++ << "is not readable by you!" << endl ++ << "Please check permissions or set the $ICEAUTHORITY variable manually before" << endl ++ << "calling dcop." << endl; ++ } ++ } ++ else ++ { ++ if( users.count() > 1 ) ++ continue; ++ else ++ { ++ cerr << "WARNING: Cannot find ICE authority file " ++ << iceFile << "!" << endl ++ << "Please check permissions or set the $ICEAUTHORITY" ++ << " variable manually before" << endl ++ << "calling dcop." << endl; ++ } ++ } ++ } ++ ++ // Main loop ++ // If users is an empty list we're calling for the currently logged ++ // in user. In this case we don't have a session, but still want ++ // to iterate the loop once. ++ QStringList::Iterator sIt = sessions.begin(); ++ for( ; sIt != sessions.end() || users.isEmpty(); sIt++ ) ++ { ++ if( !presetDCOPServer && !users.isEmpty() ) ++ { ++ QString dcopFile = it.data() + "/" + *sIt; ++ QFile f( dcopFile ); ++ if( !f.open( IO_ReadOnly ) ) ++ { ++ cerr << "Can't open " << dcopFile << " for reading!" << endl; ++ exit( -1 ); ++ } ++ ++ QStringList l( QStringList::split( '\n', f.readAll() ) ); ++ dcopServer = l.first(); ++ ++ if( dcopServer.isEmpty() ) ++ { ++ cerr << "WARNING: Unable to determine DCOP server for session " ++ << *sIt << "!" << endl ++ << "Please check permissions or set the $DCOPSERVER variable manually before" << endl ++ << "calling dcop." << endl; ++ exit( -1 ); ++ } ++ } ++ ++ delete client; ++ client = new DCOPClient; ++ if( !dcopServer.isEmpty() ) ++ client->setServerAddress( dcopServer.ascii() ); ++ bool success = client->attach(); ++ if( !success ) ++ { ++ cerr << "ERROR: Couldn't attach to DCOP server!" << endl; ++ continue; ++ } ++ dcop = client; ++ ++ switch ( args.count() ) ++ { ++ case 0: ++ queryApplications(""); ++ break; ++ case 1: ++ if (endsWith(app, '*')) ++ queryApplications(app); ++ else ++ queryObjects( app, "" ); ++ break; ++ case 2: ++ if (endsWith(objid, '*')) ++ queryObjects(app, objid); ++ else ++ queryFunctions( app, objid ); ++ break; ++ case 3: ++ default: ++ if( readStdin ) ++ { ++ QCStringList::Iterator replaceArg = args.end(); ++ ++ QCStringList::Iterator it; ++ for( it = args.begin(); it != args.end(); it++ ) ++ if( *it == "%1" ) ++ replaceArg = it; ++ ++ // Read from stdin until EOF and call function for each line read ++ char *buf = new char[ 1000 ]; ++ while ( !feof( stdin ) ) ++ { ++ fgets( buf, 1000, stdin ); ++ ++ if( replaceArg != args.end() ) ++ *replaceArg = buf; ++ ++ callFunction( app, objid, function, params ); ++ } ++ } ++ else ++ { ++ // Just call function ++ // cout << "call " << app << ", " << objid << ", " << function << ", (params)" << endl; ++ callFunction( app, objid, function, params ); ++ } ++ break; ++ } ++ // Another sIt++ would make the loop infinite... ++ if( users.isEmpty() ) ++ break; ++ } ++ ++ // Another it++ would make the loop infinite... ++ if( it == users.end() ) ++ break; + } ++ } ++ ++ ++ int main( int argc, char** argv ) ++ { ++ bool readStdin = false; ++ int numOptions = 0; ++ QString user; ++ Session session = DefaultSession; ++ QString sessionName; ++ ++ // Scan for command-line options first ++ for( int pos = 1 ; pos <= argc - 1 ; pos++ ) ++ { ++ if( strcmp( argv[ pos ], "--help" ) == 0 ) ++ showHelp( 0 ); ++ else if( strcmp( argv[ pos ], "--pipe" ) == 0 ) ++ { ++ readStdin = true; ++ numOptions++; ++ } ++ else if( strcmp( argv[ pos ], "--user" ) == 0 ) ++ { ++ if( pos <= argc - 2 ) ++ { ++ user = QString::fromLocal8Bit( argv[ pos + 1] ); ++ numOptions +=2; ++ pos++; ++ } ++ else ++ { ++ cerr << "Missing username for '--user' option!" << endl << endl; ++ showHelp( -1 ); ++ } ++ } ++ else if( strcmp( argv[ pos ], "--all-users" ) == 0 ) ++ { ++ user = "*"; ++ numOptions ++; ++ } ++ else if( strcmp( argv[ pos ], "--list-sessions" ) == 0 ) ++ { ++ session = QuerySessions; ++ numOptions ++; ++ } ++ else if( strcmp( argv[ pos ], "--all-sessions" ) == 0 ) ++ { ++ session = AllSessions; ++ numOptions ++; ++ } ++ else if( argv[ pos ][ 0 ] == '-' ) ++ { ++ cerr << "Unknown command-line option '" << argv[ pos ] ++ << "'." << endl << endl; ++ showHelp( -1 ); ++ } ++ else ++ break; // End of options ++ } ++ ++ argc -= numOptions; ++ ++ QCStringList args; ++ for( int i = numOptions; i < argc + numOptions - 1; i++ ) ++ args.append( argv[ i + 1 ] ); ++ ++ if( readStdin && args.count() < 3 ) ++ { ++ cerr << "--pipe option only supported for function calls!" << endl << endl; ++ showHelp( -1 ); ++ } ++ ++ if( user == "*" && args.count() < 3 && session != QuerySessions ) ++ { ++ cerr << "ERROR: The --all-users option is only supported for function calls!" << endl << endl; ++ showHelp( -1 ); ++ } ++ ++ if( session == QuerySessions && !args.isEmpty() ) ++ { ++ cerr << "ERROR: The --list-sessions option cannot be used for actual DCOP calls!" << endl << endl; ++ showHelp( -1 ); ++ } ++ ++ if( session == QuerySessions && user.isEmpty() ) ++ { ++ cerr << "ERROR: The --list-sessions option can only be used with the --user or" << endl ++ << "--all-users options!" << endl << endl; ++ showHelp( -1 ); ++ } ++ ++ if( session != DefaultSession && session != QuerySessions && ++ args.count() < 3 ) ++ { ++ cerr << "ERROR: The --session and --all-sessions options are only supported for function" << endl ++ << "calls!" << endl << endl; ++ showHelp( -1 ); ++ } ++ ++ UserList users; ++ if( user == "*" ) ++ users = userList(); ++ else if( !user.isEmpty() ) ++ users[ user ] = userList()[ user ]; ++ ++ runDCOP( args, users, session, sessionName, readStdin ); + + return 0; + } ++ ++ // vim: set ts=8 sts=4 sw=4 noet: ++ +diff -cr dcop/client/dcopfind.cpp dcop2/client/dcopfind.cpp +*** dcop/client/dcopfind.cpp Wed Jan 30 22:38:07 2002 +--- dcop2/client/dcopfind.cpp Wed Jan 30 22:37:04 2002 +*************** +*** 36,42 **** + static bool bAppIdOnly = 0; + static bool bLaunchApp = 0; + +! bool findObject( const char* app, const char* obj, const char* func, int argc, char** args ) + { + QString f = func; // Qt is better with unicode strings, so use one. + int left = f.find( '(' ); +--- 36,42 ---- + static bool bAppIdOnly = 0; + static bool bLaunchApp = 0; + +! bool findObject( const char* app, const char* obj, const char* func, QCStringList args ) + { + QString f = func; // Qt is better with unicode strings, so use one. + int left = f.find( '(' ); +*************** +*** 118,124 **** + f = fc; + } + +! if ( (int) types.count() != argc ) { + qWarning( "arguments do not match" ); + exit(1); + } +--- 118,124 ---- + f = fc; + } + +! if ( types.count() != args.count() ) { + qWarning( "arguments do not match" ); + exit(1); + } +*************** +*** 128,136 **** + + int i = 0; + for ( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) { +! marshall(arg, argc, args, i, *it); + } +! if ( (int) i != argc ) { + qWarning( "arguments do not match" ); + exit(1); + } +--- 128,136 ---- + + int i = 0; + for ( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) { +! marshall(arg, args, i, *it); + } +! if ( (uint) i != args.count() ) { + qWarning( "arguments do not match" ); + exit(1); + } +*************** +*** 221,227 **** + argc = 0; + } + +! findObject( app, objid, function, argc, args ); + + return 0; + } +--- 221,231 ---- + argc = 0; + } + +! QCStringList params; +! for( int i = 0; i < argc; i++ ) +! params.append( args[ i ] ); +! +! findObject( app, objid, function, params ); + + return 0; + } +diff -cr dcop/client/marshall.cpp dcop2/client/marshall.cpp +*** dcop/client/marshall.cpp Wed Jan 30 22:38:07 2002 +--- dcop2/client/marshall.cpp Wed Jan 30 22:37:04 2002 +*************** +*** 242,349 **** + + } + +! void marshall(QDataStream &arg, int argc, char **argv, int &i, QString type) + { +! if (type == "QStringList") +! type = "QValueList"; +! if (type == "QCStringList") +! type = "QValueList"; +! if (i >= argc) +! { +! qWarning("Not enough arguments."); +! exit(1); +! } +! QString s = QString::fromLocal8Bit(argv[i]); +! +! if ( type == "int" ) +! arg << s.toInt(); +! else if ( type == "uint" ) +! arg << s.toUInt(); +! else if ( type == "unsigned" ) +! arg << s.toUInt(); +! else if ( type == "unsigned int" ) +! arg << s.toUInt(); +! else if ( type == "long" ) +! arg << s.toLong(); +! else if ( type == "long int" ) +! arg << s.toLong(); +! else if ( type == "unsigned long" ) +! arg << s.toULong(); +! else if ( type == "unsigned long int" ) +! arg << s.toULong(); +! else if ( type == "float" ) +! arg << s.toFloat(); +! else if ( type == "double" ) +! arg << s.toDouble(); +! else if ( type == "bool" ) +! arg << mkBool( s ); +! else if ( type == "QString" ) +! arg << s; +! else if ( type == "QCString" ) +! arg << QCString( argv[i] ); +! else if ( type == "QColor" ) +! arg << mkColor( s ); +! else if ( type == "QPoint" ) +! arg << mkPoint( s ); +! else if ( type == "QSize" ) +! arg << mkSize( s ); +! else if ( type == "QRect" ) +! arg << mkRect( s ); +! else if ( type == "QVariant" ) { +! if ( s == "true" || s == "false" ) +! arg << QVariant( mkBool( s ), 42 ); +! else if ( s.left( 4 ) == "int(" ) +! arg << QVariant( s.mid(4, s.length()-5).toInt() ); +! else if ( s.left( 7 ) == "QPoint(" ) +! arg << QVariant( mkPoint( s.mid(7, s.length()-8) ) ); +! else if ( s.left( 6 ) == "QSize(" ) +! arg << QVariant( mkSize( s.mid(6, s.length()-7) ) ); +! else if ( s.left( 6 ) == "QRect(" ) +! arg << QVariant( mkRect( s.mid(6, s.length()-7) ) ); +! else if ( s.left( 7 ) == "QColor(" ) +! arg << QVariant( mkColor( s.mid(7, s.length()-8) ) ); +! else +! arg << QVariant( s ); +! } else if ( type.startsWith("QValueList<")) { +! type = type.mid(11, type.length() - 12); +! QStringList list; +! QString delim = s; +! if (delim == "[") +! delim = "]"; +! if (delim == "(") +! delim = ")"; +! i++; +! QByteArray dummy_data; +! QDataStream dummy_arg(dummy_data, IO_WriteOnly); + +! int j = i; +! int count = 0; +! // Parse list to get the count +! while (true) { +! if (j >= argc) +! { +! qWarning("List end-delimiter '%s' not found.", delim.latin1()); +! exit(1); +! } +! if (argv[j] == delim) break; +! marshall(dummy_arg, argc, argv, j, type); +! count++; +! } +! arg << (Q_UINT32) count; +! // Parse the list for real +! while (true) { +! if (i >= argc) +! { +! qWarning("List end-delimiter '%s' not found.", delim.latin1()); +! exit(1); +! } +! if (argv[i] == delim) break; +! marshall(arg, argc, argv, i, type); +! } +! } else { +! qWarning( "cannot handle datatype '%s'", type.latin1() ); +! exit(1); +! } + i++; + } + +--- 242,351 ---- + + } + +! void marshall( QDataStream &arg, QCStringList args, uint &i, QString type ) + { +! if (type == "QStringList") +! type = "QValueList"; +! if (type == "QCStringList") +! type = "QValueList"; +! if( i > args.count() ) +! { +! qWarning("Not enough arguments."); +! exit(1); +! } +! QString s = QString::fromLocal8Bit( args[ i ] ); + +! if ( type == "int" ) +! arg << s.toInt(); +! else if ( type == "uint" ) +! arg << s.toUInt(); +! else if ( type == "unsigned" ) +! arg << s.toUInt(); +! else if ( type == "unsigned int" ) +! arg << s.toUInt(); +! else if ( type == "long" ) +! arg << s.toLong(); +! else if ( type == "long int" ) +! arg << s.toLong(); +! else if ( type == "unsigned long" ) +! arg << s.toULong(); +! else if ( type == "unsigned long int" ) +! arg << s.toULong(); +! else if ( type == "float" ) +! arg << s.toFloat(); +! else if ( type == "double" ) +! arg << s.toDouble(); +! else if ( type == "bool" ) +! arg << mkBool( s ); +! else if ( type == "QString" ) +! arg << s; +! else if ( type == "QCString" ) +! arg << QCString( args[ i ] ); +! else if ( type == "QColor" ) +! arg << mkColor( s ); +! else if ( type == "QPoint" ) +! arg << mkPoint( s ); +! else if ( type == "QSize" ) +! arg << mkSize( s ); +! else if ( type == "QRect" ) +! arg << mkRect( s ); +! else if ( type == "QVariant" ) { +! if ( s == "true" || s == "false" ) +! arg << QVariant( mkBool( s ), 42 ); +! else if ( s.left( 4 ) == "int(" ) +! arg << QVariant( s.mid(4, s.length()-5).toInt() ); +! else if ( s.left( 7 ) == "QPoint(" ) +! arg << QVariant( mkPoint( s.mid(7, s.length()-8) ) ); +! else if ( s.left( 6 ) == "QSize(" ) +! arg << QVariant( mkSize( s.mid(6, s.length()-7) ) ); +! else if ( s.left( 6 ) == "QRect(" ) +! arg << QVariant( mkRect( s.mid(6, s.length()-7) ) ); +! else if ( s.left( 7 ) == "QColor(" ) +! arg << QVariant( mkColor( s.mid(7, s.length()-8) ) ); +! else +! arg << QVariant( s ); +! } else if ( type.startsWith("QValueList<")) { +! type = type.mid(11, type.length() - 12); +! QStringList list; +! QString delim = s; +! if (delim == "[") +! delim = "]"; +! if (delim == "(") +! delim = ")"; + i++; ++ QByteArray dummy_data; ++ QDataStream dummy_arg(dummy_data, IO_WriteOnly); ++ ++ uint j = i; ++ uint count = 0; ++ // Parse list to get the count ++ while (true) { ++ if( j > args.count() ) ++ { ++ qWarning("List end-delimiter '%s' not found.", delim.latin1()); ++ exit(1); ++ } ++ if( QString::fromLocal8Bit( args[ j ] ) == delim ) ++ break; ++ marshall( dummy_arg, args, j, type ); ++ count++; ++ } ++ arg << (Q_UINT32) count; ++ // Parse the list for real ++ while (true) { ++ if( i > args.count() ) ++ { ++ qWarning("List end-delimiter '%s' not found.", delim.latin1()); ++ exit(1); ++ } ++ if( QString::fromLocal8Bit( args[ i ] ) == delim ) ++ break; ++ marshall( arg, args, i, type ); ++ } ++ } else { ++ qWarning( "cannot handle datatype '%s'", type.latin1() ); ++ exit(1); ++ } ++ i++; + } + diff --git a/kompare/tests/diff/contextp.diff b/kompare/tests/diff/contextp.diff new file mode 100644 index 00000000..ed9319bc --- /dev/null +++ b/kompare/tests/diff/contextp.diff @@ -0,0 +1,27 @@ +*** /home/John/lao Thu Apr 12 11:09:30 2001 +--- /home/John/tzu Sat Jul 28 13:23:25 2001 +*************** +*** 1,7 **** +- The Way that can be told of is not the eternal Way; +- The name that can be named is not the eternal name. + The Nameless is the origin of Heaven and Earth; +! The Named is the mother of all things. + Therefore let there always be non-being, + so we may see their subtlety, + And let there always be being, +--- 1,6 ---- + The Nameless is the origin of Heaven and Earth; +! The named is the mother of all things. +! + Therefore let there always be non-being, + so we may see their subtlety, + And let there always be being, +*************** And let there always be being, +*** 9,11 **** +--- 8,13 ---- + The two are the same, + But after they are produced, + they have different names. ++ They both may be called deep and profound. ++ Deeper and more profound, ++ The door of all subtleties! diff --git a/kompare/tests/diff/ed.diff b/kompare/tests/diff/ed.diff new file mode 100644 index 00000000..43c2b2f1 --- /dev/null +++ b/kompare/tests/diff/ed.diff @@ -0,0 +1,10 @@ +11a +They both may be called deep and profound. +Deeper and more profound, +The door of all subtleties! +. +4c +The named is the mother of all things. + +. +1,2d diff --git a/kompare/tests/diff/edm.diff b/kompare/tests/diff/edm.diff new file mode 100644 index 00000000..0df2abc1 --- /dev/null +++ b/kompare/tests/diff/edm.diff @@ -0,0 +1,680 @@ +diff -er dcop/client/dcop.cpp dcop2/client/dcop.cpp +343a + +// vim: set ts=8 sts=4 sw=4 noet: + +. +340a +} + + +int main( int argc, char** argv ) +{ + bool readStdin = false; + int numOptions = 0; + QString user; + Session session = DefaultSession; + QString sessionName; + + // Scan for command-line options first + for( int pos = 1 ; pos <= argc - 1 ; pos++ ) + { + if( strcmp( argv[ pos ], "--help" ) == 0 ) + showHelp( 0 ); + else if( strcmp( argv[ pos ], "--pipe" ) == 0 ) + { + readStdin = true; + numOptions++; + } + else if( strcmp( argv[ pos ], "--user" ) == 0 ) + { + if( pos <= argc - 2 ) + { + user = QString::fromLocal8Bit( argv[ pos + 1] ); + numOptions +=2; + pos++; + } + else + { + cerr << "Missing username for '--user' option!" << endl << endl; + showHelp( -1 ); + } + } + else if( strcmp( argv[ pos ], "--all-users" ) == 0 ) + { + user = "*"; + numOptions ++; + } + else if( strcmp( argv[ pos ], "--list-sessions" ) == 0 ) + { + session = QuerySessions; + numOptions ++; + } + else if( strcmp( argv[ pos ], "--all-sessions" ) == 0 ) + { + session = AllSessions; + numOptions ++; + } + else if( argv[ pos ][ 0 ] == '-' ) + { + cerr << "Unknown command-line option '" << argv[ pos ] + << "'." << endl << endl; + showHelp( -1 ); + } + else + break; // End of options + } + + argc -= numOptions; + + QCStringList args; + for( int i = numOptions; i < argc + numOptions - 1; i++ ) + args.append( argv[ i + 1 ] ); + + if( readStdin && args.count() < 3 ) + { + cerr << "--pipe option only supported for function calls!" << endl << endl; + showHelp( -1 ); + } + + if( user == "*" && args.count() < 3 && session != QuerySessions ) + { + cerr << "ERROR: The --all-users option is only supported for function calls!" << endl << endl; + showHelp( -1 ); + } + + if( session == QuerySessions && !args.isEmpty() ) + { + cerr << "ERROR: The --list-sessions option cannot be used for actual DCOP calls!" << endl << endl; + showHelp( -1 ); + } + + if( session == QuerySessions && user.isEmpty() ) + { + cerr << "ERROR: The --list-sessions option can only be used with the --user or" << endl + << "--all-users options!" << endl << endl; + showHelp( -1 ); + } + + if( session != DefaultSession && session != QuerySessions && + args.count() < 3 ) + { + cerr << "ERROR: The --session and --all-sessions options are only supported for function" << endl + << "calls!" << endl << endl; + showHelp( -1 ); + } + + UserList users; + if( user == "*" ) + users = userList(); + else if( !user.isEmpty() ) + users[ user ] = userList()[ user ]; + + runDCOP( args, users, session, sessionName, readStdin ); +. +339a + if( users.count() > 1 || ( users.count() == 1 && + ( getenv( "ICEAUTHORITY" ) == 0 || getenv( "DISPLAY" ) == 0 ) ) ) + { + // Check for ICE authority file and if the file can be read by us + QString home = it.data(); + QString iceFile = it.data() + "/.ICEauthority"; + QFileInfo fi( iceFile ); + if( iceFile.isEmpty() ) + { + cerr << "WARNING: Cannot determine home directory for user " + << it.key() << "!" << endl + << "Please check permissions or set the $ICEAUTHORITY variable manually before" << endl + << "calling dcop." << endl; + } + else if( fi.exists() ) + { + if( fi.isReadable() ) + { + char *envStr = strdup( ( "ICEAUTHORITY=" + iceFile ).ascii() ); + putenv( envStr ); + //cerr << "ice: " << envStr << endl; + } + else + { + cerr << "WARNING: ICE authority file " << iceFile + << "is not readable by you!" << endl + << "Please check permissions or set the $ICEAUTHORITY variable manually before" << endl + << "calling dcop." << endl; + } + } + else + { + if( users.count() > 1 ) + continue; + else + { + cerr << "WARNING: Cannot find ICE authority file " + << iceFile << "!" << endl + << "Please check permissions or set the $ICEAUTHORITY" + << " variable manually before" << endl + << "calling dcop." << endl; + } + } + } + + // Main loop + // If users is an empty list we're calling for the currently logged + // in user. In this case we don't have a session, but still want + // to iterate the loop once. + QStringList::Iterator sIt = sessions.begin(); + for( ; sIt != sessions.end() || users.isEmpty(); sIt++ ) + { + if( !presetDCOPServer && !users.isEmpty() ) + { + QString dcopFile = it.data() + "/" + *sIt; + QFile f( dcopFile ); + if( !f.open( IO_ReadOnly ) ) + { + cerr << "Can't open " << dcopFile << " for reading!" << endl; + exit( -1 ); + } + + QStringList l( QStringList::split( '\n', f.readAll() ) ); + dcopServer = l.first(); + + if( dcopServer.isEmpty() ) + { + cerr << "WARNING: Unable to determine DCOP server for session " + << *sIt << "!" << endl + << "Please check permissions or set the $DCOPSERVER variable manually before" << endl + << "calling dcop." << endl; + exit( -1 ); + } + } + + delete client; + client = new DCOPClient; + if( !dcopServer.isEmpty() ) + client->setServerAddress( dcopServer.ascii() ); + bool success = client->attach(); + if( !success ) + { + cerr << "ERROR: Couldn't attach to DCOP server!" << endl; + continue; + } + dcop = client; + + switch ( args.count() ) + { + case 0: + queryApplications(""); + break; + case 1: + if (endsWith(app, '*')) + queryApplications(app); + else + queryObjects( app, "" ); + break; + case 2: + if (endsWith(objid, '*')) + queryObjects(app, objid); + else + queryFunctions( app, objid ); + break; + case 3: + default: + if( readStdin ) + { + QCStringList::Iterator replaceArg = args.end(); + + QCStringList::Iterator it; + for( it = args.begin(); it != args.end(); it++ ) + if( *it == "%1" ) + replaceArg = it; + + // Read from stdin until EOF and call function for each line read + char *buf = new char[ 1000 ]; + while ( !feof( stdin ) ) + { + fgets( buf, 1000, stdin ); + + if( replaceArg != args.end() ) + *replaceArg = buf; + + callFunction( app, objid, function, params ); + } + } + else + { + // Just call function +// cout << "call " << app << ", " << objid << ", " << function << ", (params)" << endl; + callFunction( app, objid, function, params ); + } + break; + } + // Another sIt++ would make the loop infinite... + if( users.isEmpty() ) + break; + } + + // Another it++ would make the loop infinite... + if( it == users.end() ) + break; +. +308,338c + if( !args.isEmpty() ) + app = args[ 0 ]; + if( args.count() > 1 ) + objid = args[ 1 ]; + if( args.count() > 2 ) + function = args[ 2 ]; + if( args.count() > 3) + { + params = args; + params.remove( params.begin() ); + params.remove( params.begin() ); + params.remove( params.begin() ); + } + } + + bool firstRun = true; + UserList::Iterator it; + QStringList sessions; + bool presetDCOPServer = false; +// char *dcopStr = 0L; + QString dcopServer; + + for( it = users.begin(); it != users.end() || firstRun; it++ ) + { + firstRun = false; + + //cout << "Iterating '" << it.key() << "'" << endl; + + if( session == QuerySessions ) + { + QStringList sessions = dcopSessionList( it.key(), it.data() ); + if( sessions.isEmpty() ) + { + cout << "No active sessions"; + if( !( *it ).isEmpty() ) + cout << " for user " << *it; + cout << endl; + } + else + { + cout << "Active sessions "; + if( !( *it ).isEmpty() ) + cout << "for user " << *it << " "; + cout << ":" << endl; + + QStringList::Iterator sIt; + for( sIt = sessions.begin(); sIt != sessions.end(); sIt++ ) + cout << " " << *sIt << endl; + + cout << endl; + } + continue; + } + + if( getenv( "DCOPSERVER" ) ) + { + sessions.append( getenv( "DCOPSERVER" ) ); + presetDCOPServer = true; + } + + if( users.count() > 1 || ( users.count() == 1 && + ( getenv( "DCOPSERVER" ) == 0 /*&& getenv( "DISPLAY" ) == 0*/ ) ) ) + { + sessions = dcopSessionList( it.key(), it.data() ); + if( sessions.isEmpty() ) + { + if( users.count() > 1 ) + continue; + else + { + cerr << "ERROR: No active KDE sessions!" << endl + << "If you are sure there is one, please set the $DCOPSERVER variable manually" << endl + << "before calling dcop." << endl; + exit( -1 ); + } + } + else if( sessions.count() > 1 && session != AllSessions ) + { + cerr << "ERROR: Multiple available KDE sessions!" << endl + << "Please specify the correct session to use with --session or use the" << endl + << "--all-sessions option to broadcast to all sessions." << endl; + exit( -1 ); + } + } +. +289,304c + // WARNING: This part (until the closing '}') could very + // well be broken now. As I don't know how to trigger and test + // dcoprefs this code is *not* tested. It compiles and it looks + // ok to me, but that's all I can say - Martijn (2001/12/24) + int delimPos = args[ 0 ].findRev( ',' ); + if( delimPos == -1 ) + { + cerr << "Error: '" << args[ 0 ] + << "' is not a valid DCOP reference." << endl; + exit( -1 ); + } + args[ 0 ][ delimPos ] = 0; + app = args[ 0 ].mid( 8 ); + delimPos++; + args[ 0 ][ args[ 0 ].length() - 1 ] = 0; + objid = args[ 0 ].mid( delimPos ); + if( args.count() > 1 ) + function = args[ 1 ]; + if( args.count() > 2 ) + { + params = args; + params.remove( params.begin() ); + params.remove( params.begin() ); + } +. +286,287c + QCStringList params; + DCOPClient *client = 0L; + if ( !args.isEmpty() && args[ 0 ].find( "DCOPRef(" ) == 0 ) +. +282a +/** + * Do the actual DCOP call + */ +void runDCOP( QCStringList args, UserList users, Session session, + const QString sessionName, bool readStdin ) +{ +. +279,281c + return result; +} + +/** + * Return a list of available DCOP sessions for the specified user + * An empty list means no sessions are available, or an error occurred. + */ +QStringList dcopSessionList( const QString &user, const QString &home ) +{ + if( home.isEmpty() ) + { + cerr << "WARNING: Cannot determine home directory for user " + << user << "!" << endl + << "Please check permissions or set the $DCOPSERVER variable manually before" << endl + << "calling dcop." << endl; + return QStringList(); + } + + QStringList result; + QFileInfo dirInfo( home ); + if( !dirInfo.exists() || !dirInfo.isReadable() ) + return result; + + QDir d( home ); + d.setFilter( QDir::Files | QDir::Hidden | QDir::NoSymLinks ); + d.setNameFilter( ".DCOPserver*" ); + + const QFileInfoList *list = d.entryInfoList(); + if( !list ) + return result; + + QFileInfoListIterator it( *list ); + QFileInfo *fi; + + while ( ( fi = it.current() ) != 0 ) + { + if( fi->isReadable() ) + result.append( fi->fileName() ); + ++it; + } + return result; +} +. +274,276c + QStringList l( QStringList::split( '\n', f.readAll() ) ); + + for( QStringList::ConstIterator it( l.begin() ); it != l.end(); ++it ) + { + QStringList userInfo( QStringList::split( ':', *it, true ) ); + result[ userInfo[ 0 ] ] = userInfo[ 5 ]; +. +272a + UserList result; + + QFile f( "/etc/passwd" ); + + if( !f.open( IO_ReadOnly ) ) + { + cerr << "Can't open /etc/passwd for reading!" << endl; + return result; + } +. +270,271c +/** + * Return a list of all users and their home directories. + * Returns an empty list if /etc/passwd cannot be read for some reason. + */ +static UserList userList() +. +268a +/** + * Show command-line help and exit + */ +void showHelp( int exitCode = 0 ) +{ + cout << "Usage: dcop [options] [application [object [function [arg1] [arg2] ... ] ] ]" << endl + << "" << endl + << "Console DCOP client" << endl + << "" << endl + << "Generic options:" << endl + << " --help Show help about options" << endl + << "" << endl + << "Options:" << endl + << " --pipe Call DCOP for each line read from stdin" << endl + << " --user Connect to the given user's DCOP server. This option will" << endl + << " ignore the values of the environment vars $DCOPSERVER and" << endl + << " $ICEAUTHORITY, even if they are set." << endl + << " If the user has more than one open session, you must also" << endl + << " use one of the --list-sessions, --session or --als-sessions" << endl + << " command-line options." << endl + << " --all-users Send the same DCOP call to all users with a running DCOP" << endl + << " server. Only failed calls to existing DCOP servers will" + << " generate an error message. If no DCOP server is available" << endl + << " at all, no error will be generated." << endl; + + exit( exitCode ); +} +. +246,250c + uint i = 0; + for( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) + marshall( arg, args, i, *it ); + + if ( i != args.count() ) + { +. +164c +// exit(1); + return; +. +156,157c + uint a = (*it).contains(','); + if ( ( a == 0 && args.isEmpty() ) || ( a > 0 && a + 1 == args.count() ) ) +. +139c + if ( !ok && args.isEmpty() ) +. +123d +121c +void callFunction( const char* app, const char* obj, const char* func, const QCStringList args ) +. +35a +static QTextStream cout( stdout, IO_WriteOnly ); +static QTextStream cerr( stderr, IO_WriteOnly ); + +/** + * Session to send call to + * DefaultSession - current session. Current KDE session when called without + * --user or --all-users option. Otherwise this value ignores + * all users with more than one active session. + * AllSessions - Send to all sessions found. requires --user or --all-users. + * QuerySessions - Don't call DCOP, return a list of available sessions. + * CustomSession - Use the specified session + */ +enum Session { DefaultSession = 0, AllSessions, QuerySessions, CustomSession }; + +. +33a +typedef QMap UserList; + +. +28,30c +#include "../kdatastream.h" +. +25c +#include +#include +#include +#include +#include +#include +#include + +// putenv() is not available on all platforms, so make sure the emulation +// wrapper is available in those cases by loading config.h! +#include + +. +23c +#include +#include +#include + +. +diff -er dcop/client/dcopfind.cpp dcop2/client/dcopfind.cpp +224c + QCStringList params; + for( int i = 0; i < argc; i++ ) + params.append( args[ i ] ); + + findObject( app, objid, function, params ); +. +133c + if ( (uint) i != args.count() ) { +. +131c + marshall(arg, args, i, *it); +. +121c + if ( types.count() != args.count() ) { +. +39c +bool findObject( const char* app, const char* obj, const char* func, QCStringList args ) +. +diff -er dcop/client/marshall.cpp dcop2/client/marshall.cpp +347a + QByteArray dummy_data; + QDataStream dummy_arg(dummy_data, IO_WriteOnly); + + uint j = i; + uint count = 0; + // Parse list to get the count + while (true) { + if( j > args.count() ) + { + qWarning("List end-delimiter '%s' not found.", delim.latin1()); + exit(1); + } + if( QString::fromLocal8Bit( args[ j ] ) == delim ) + break; + marshall( dummy_arg, args, j, type ); + count++; + } + arg << (Q_UINT32) count; + // Parse the list for real + while (true) { + if( i > args.count() ) + { + qWarning("List end-delimiter '%s' not found.", delim.latin1()); + exit(1); + } + if( QString::fromLocal8Bit( args[ i ] ) == delim ) + break; + marshall( arg, args, i, type ); + } + } else { + qWarning( "cannot handle datatype '%s'", type.latin1() ); + exit(1); + } + i++; +. +319,346c + if ( type == "int" ) + arg << s.toInt(); + else if ( type == "uint" ) + arg << s.toUInt(); + else if ( type == "unsigned" ) + arg << s.toUInt(); + else if ( type == "unsigned int" ) + arg << s.toUInt(); + else if ( type == "long" ) + arg << s.toLong(); + else if ( type == "long int" ) + arg << s.toLong(); + else if ( type == "unsigned long" ) + arg << s.toULong(); + else if ( type == "unsigned long int" ) + arg << s.toULong(); + else if ( type == "float" ) + arg << s.toFloat(); + else if ( type == "double" ) + arg << s.toDouble(); + else if ( type == "bool" ) + arg << mkBool( s ); + else if ( type == "QString" ) + arg << s; + else if ( type == "QCString" ) + arg << QCString( args[ i ] ); + else if ( type == "QColor" ) + arg << mkColor( s ); + else if ( type == "QPoint" ) + arg << mkPoint( s ); + else if ( type == "QSize" ) + arg << mkSize( s ); + else if ( type == "QRect" ) + arg << mkRect( s ); + else if ( type == "QVariant" ) { + if ( s == "true" || s == "false" ) + arg << QVariant( mkBool( s ), 42 ); + else if ( s.left( 4 ) == "int(" ) + arg << QVariant( s.mid(4, s.length()-5).toInt() ); + else if ( s.left( 7 ) == "QPoint(" ) + arg << QVariant( mkPoint( s.mid(7, s.length()-8) ) ); + else if ( s.left( 6 ) == "QSize(" ) + arg << QVariant( mkSize( s.mid(6, s.length()-7) ) ); + else if ( s.left( 6 ) == "QRect(" ) + arg << QVariant( mkRect( s.mid(6, s.length()-7) ) ); + else if ( s.left( 7 ) == "QColor(" ) + arg << QVariant( mkColor( s.mid(7, s.length()-8) ) ); + else + arg << QVariant( s ); + } else if ( type.startsWith("QValueList<")) { + type = type.mid(11, type.length() - 12); + QStringList list; + QString delim = s; + if (delim == "[") + delim = "]"; + if (delim == "(") + delim = ")"; +. +247,317c + if (type == "QStringList") + type = "QValueList"; + if (type == "QCStringList") + type = "QValueList"; + if( i > args.count() ) + { + qWarning("Not enough arguments."); + exit(1); + } + QString s = QString::fromLocal8Bit( args[ i ] ); +. +245c +void marshall( QDataStream &arg, QCStringList args, uint &i, QString type ) +. diff --git a/kompare/tests/diff/normal.diff b/kompare/tests/diff/normal.diff new file mode 100644 index 00000000..6f82a1c4 --- /dev/null +++ b/kompare/tests/diff/normal.diff @@ -0,0 +1,12 @@ +1,2d0 +< The Way that can be told of is not the eternal Way; +< The name that can be named is not the eternal name. +4c2,3 +< The Named is the mother of all things. +--- +> The named is the mother of all things. +> +11a11,13 +> They both may be called deep and profound. +> Deeper and more profound, +> The door of all subtleties! diff --git a/kompare/tests/diff/normalm.diff b/kompare/tests/diff/normalm.diff new file mode 100644 index 00000000..3db667ba --- /dev/null +++ b/kompare/tests/diff/normalm.diff @@ -0,0 +1,849 @@ +diff -r dcop/client/dcop.cpp dcop2/client/dcop.cpp +23c23,26 +< #include +--- +> #include +> #include +> #include +> +25c28,39 +< #include "../kdatastream.h" +--- +> #include +> #include +> #include +> #include +> #include +> #include +> #include +> +> // putenv() is not available on all platforms, so make sure the emulation +> // wrapper is available in those cases by loading config.h! +> #include +> +28,30c42 +< #include +< #include +< #include +--- +> #include "../kdatastream.h" +33a46,47 +> typedef QMap UserList; +> +35a50,63 +> static QTextStream cout( stdout, IO_WriteOnly ); +> static QTextStream cerr( stderr, IO_WriteOnly ); +> +> /** +> * Session to send call to +> * DefaultSession - current session. Current KDE session when called without +> * --user or --all-users option. Otherwise this value ignores +> * all users with more than one active session. +> * AllSessions - Send to all sessions found. requires --user or --all-users. +> * QuerySessions - Don't call DCOP, return a list of available sessions. +> * CustomSession - Use the specified session +> */ +> enum Session { DefaultSession = 0, AllSessions, QuerySessions, CustomSession }; +> +121c149 +< void callFunction( const char* app, const char* obj, const char* func, int argc, char** args ) +--- +> void callFunction( const char* app, const char* obj, const char* func, const QCStringList args ) +123d150 +< +139c166 +< if ( !ok && argc == 0 ) +--- +> if ( !ok && args.isEmpty() ) +156,157c183,184 +< int a = (*it).contains(','); +< if ( ( a == 0 && argc == 0) || ( a > 0 && a + 1 == argc ) ) +--- +> uint a = (*it).contains(','); +> if ( ( a == 0 && args.isEmpty() ) || ( a > 0 && a + 1 == args.count() ) ) +164c191,192 +< exit(1); +--- +> // exit(1); +> return; +246,250c274,279 +< int i = 0; +< for ( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) { +< marshall(arg, argc, args, i, *it); +< } +< if ( i != argc ) { +--- +> uint i = 0; +> for( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) +> marshall( arg, args, i, *it ); +> +> if ( i != args.count() ) +> { +268a298,324 +> /** +> * Show command-line help and exit +> */ +> void showHelp( int exitCode = 0 ) +> { +> cout << "Usage: dcop [options] [application [object [function [arg1] [arg2] ... ] ] ]" << endl +> << "" << endl +> << "Console DCOP client" << endl +> << "" << endl +> << "Generic options:" << endl +> << " --help Show help about options" << endl +> << "" << endl +> << "Options:" << endl +> << " --pipe Call DCOP for each line read from stdin" << endl +> << " --user Connect to the given user's DCOP server. This option will" << endl +> << " ignore the values of the environment vars $DCOPSERVER and" << endl +> << " $ICEAUTHORITY, even if they are set." << endl +> << " If the user has more than one open session, you must also" << endl +> << " use one of the --list-sessions, --session or --als-sessions" << endl +> << " command-line options." << endl +> << " --all-users Send the same DCOP call to all users with a running DCOP" << endl +> << " server. Only failed calls to existing DCOP servers will" +> << " generate an error message. If no DCOP server is available" << endl +> << " at all, no error will be generated." << endl; +> +> exit( exitCode ); +> } +270,271c326,330 +< +< int main( int argc, char** argv ) +--- +> /** +> * Return a list of all users and their home directories. +> * Returns an empty list if /etc/passwd cannot be read for some reason. +> */ +> static UserList userList() +272a332,340 +> UserList result; +> +> QFile f( "/etc/passwd" ); +> +> if( !f.open( IO_ReadOnly ) ) +> { +> cerr << "Can't open /etc/passwd for reading!" << endl; +> return result; +> } +274,276c342,347 +< if ( argc > 1 && argv[1][0] == '-' ) { +< fprintf( stderr, "Usage: dcop [ application [object [function [arg1] [arg2] [arg3] ... ] ] ] \n" ); +< exit(0); +--- +> QStringList l( QStringList::split( '\n', f.readAll() ) ); +> +> for( QStringList::ConstIterator it( l.begin() ); it != l.end(); ++it ) +> { +> QStringList userInfo( QStringList::split( ':', *it, true ) ); +> result[ userInfo[ 0 ] ] = userInfo[ 5 ]; +279,281c350,391 +< DCOPClient client; +< client.attach(); +< dcop = &client; +--- +> return result; +> } +> +> /** +> * Return a list of available DCOP sessions for the specified user +> * An empty list means no sessions are available, or an error occurred. +> */ +> QStringList dcopSessionList( const QString &user, const QString &home ) +> { +> if( home.isEmpty() ) +> { +> cerr << "WARNING: Cannot determine home directory for user " +> << user << "!" << endl +> << "Please check permissions or set the $DCOPSERVER variable manually before" << endl +> << "calling dcop." << endl; +> return QStringList(); +> } +> +> QStringList result; +> QFileInfo dirInfo( home ); +> if( !dirInfo.exists() || !dirInfo.isReadable() ) +> return result; +> +> QDir d( home ); +> d.setFilter( QDir::Files | QDir::Hidden | QDir::NoSymLinks ); +> d.setNameFilter( ".DCOPserver*" ); +> +> const QFileInfoList *list = d.entryInfoList(); +> if( !list ) +> return result; +> +> QFileInfoListIterator it( *list ); +> QFileInfo *fi; +> +> while ( ( fi = it.current() ) != 0 ) +> { +> if( fi->isReadable() ) +> result.append( fi->fileName() ); +> ++it; +> } +> return result; +> } +282a393,398 +> /** +> * Do the actual DCOP call +> */ +> void runDCOP( QCStringList args, UserList users, Session session, +> const QString sessionName, bool readStdin ) +> { +286,287c402,404 +< char **args = 0; +< if ((argc > 1) && (strncmp(argv[1], "DCOPRef(", 8)) == 0) +--- +> QCStringList params; +> DCOPClient *client = 0L; +> if ( !args.isEmpty() && args[ 0 ].find( "DCOPRef(" ) == 0 ) +289,304c406,429 +< char *delim = strchr(argv[1], ','); +< if (!delim) +< { +< fprintf(stderr, "Error: '%s' is not a valid DCOP reference.\n", argv[1]); +< return 1; +< } +< *delim = 0; +< app = argv[1] + 8; +< delim++; +< delim[strlen(delim)-1] = 0; +< objid = delim; +< if (argc > 2) +< function = argv[2]; +< if (argc > 3) +< args = &argv[3]; +< argc++; +--- +> // WARNING: This part (until the closing '}') could very +> // well be broken now. As I don't know how to trigger and test +> // dcoprefs this code is *not* tested. It compiles and it looks +> // ok to me, but that's all I can say - Martijn (2001/12/24) +> int delimPos = args[ 0 ].findRev( ',' ); +> if( delimPos == -1 ) +> { +> cerr << "Error: '" << args[ 0 ] +> << "' is not a valid DCOP reference." << endl; +> exit( -1 ); +> } +> args[ 0 ][ delimPos ] = 0; +> app = args[ 0 ].mid( 8 ); +> delimPos++; +> args[ 0 ][ args[ 0 ].length() - 1 ] = 0; +> objid = args[ 0 ].mid( delimPos ); +> if( args.count() > 1 ) +> function = args[ 1 ]; +> if( args.count() > 2 ) +> { +> params = args; +> params.remove( params.begin() ); +> params.remove( params.begin() ); +> } +308,338c433,516 +< if (argc > 1) +< app = argv[1]; +< if (argc > 2) +< objid = argv[2]; +< if (argc > 3) +< function = argv[3]; +< if (argc > 4) +< args = &argv[4]; +< } +< +< switch ( argc ) { +< case 0: +< case 1: +< queryApplications(""); +< break; +< case 2: +< if (endsWith(app, '*')) +< queryApplications(app); +< else +< queryObjects( app, "" ); +< break; +< case 3: +< if (endsWith(objid, '*')) +< queryObjects(app, objid); +< else +< queryFunctions( app, objid ); +< break; +< case 4: +< default: +< callFunction( app, objid, function, argc - 4, args ); +< break; +--- +> if( !args.isEmpty() ) +> app = args[ 0 ]; +> if( args.count() > 1 ) +> objid = args[ 1 ]; +> if( args.count() > 2 ) +> function = args[ 2 ]; +> if( args.count() > 3) +> { +> params = args; +> params.remove( params.begin() ); +> params.remove( params.begin() ); +> params.remove( params.begin() ); +> } +> } +> +> bool firstRun = true; +> UserList::Iterator it; +> QStringList sessions; +> bool presetDCOPServer = false; +> // char *dcopStr = 0L; +> QString dcopServer; +> +> for( it = users.begin(); it != users.end() || firstRun; it++ ) +> { +> firstRun = false; +> +> //cout << "Iterating '" << it.key() << "'" << endl; +> +> if( session == QuerySessions ) +> { +> QStringList sessions = dcopSessionList( it.key(), it.data() ); +> if( sessions.isEmpty() ) +> { +> cout << "No active sessions"; +> if( !( *it ).isEmpty() ) +> cout << " for user " << *it; +> cout << endl; +> } +> else +> { +> cout << "Active sessions "; +> if( !( *it ).isEmpty() ) +> cout << "for user " << *it << " "; +> cout << ":" << endl; +> +> QStringList::Iterator sIt; +> for( sIt = sessions.begin(); sIt != sessions.end(); sIt++ ) +> cout << " " << *sIt << endl; +> +> cout << endl; +> } +> continue; +> } +> +> if( getenv( "DCOPSERVER" ) ) +> { +> sessions.append( getenv( "DCOPSERVER" ) ); +> presetDCOPServer = true; +> } +> +> if( users.count() > 1 || ( users.count() == 1 && +> ( getenv( "DCOPSERVER" ) == 0 /*&& getenv( "DISPLAY" ) == 0*/ ) ) ) +> { +> sessions = dcopSessionList( it.key(), it.data() ); +> if( sessions.isEmpty() ) +> { +> if( users.count() > 1 ) +> continue; +> else +> { +> cerr << "ERROR: No active KDE sessions!" << endl +> << "If you are sure there is one, please set the $DCOPSERVER variable manually" << endl +> << "before calling dcop." << endl; +> exit( -1 ); +> } +> } +> else if( sessions.count() > 1 && session != AllSessions ) +> { +> cerr << "ERROR: Multiple available KDE sessions!" << endl +> << "Please specify the correct session to use with --session or use the" << endl +> << "--all-sessions option to broadcast to all sessions." << endl; +> exit( -1 ); +> } +> } +339a518,660 +> if( users.count() > 1 || ( users.count() == 1 && +> ( getenv( "ICEAUTHORITY" ) == 0 || getenv( "DISPLAY" ) == 0 ) ) ) +> { +> // Check for ICE authority file and if the file can be read by us +> QString home = it.data(); +> QString iceFile = it.data() + "/.ICEauthority"; +> QFileInfo fi( iceFile ); +> if( iceFile.isEmpty() ) +> { +> cerr << "WARNING: Cannot determine home directory for user " +> << it.key() << "!" << endl +> << "Please check permissions or set the $ICEAUTHORITY variable manually before" << endl +> << "calling dcop." << endl; +> } +> else if( fi.exists() ) +> { +> if( fi.isReadable() ) +> { +> char *envStr = strdup( ( "ICEAUTHORITY=" + iceFile ).ascii() ); +> putenv( envStr ); +> //cerr << "ice: " << envStr << endl; +> } +> else +> { +> cerr << "WARNING: ICE authority file " << iceFile +> << "is not readable by you!" << endl +> << "Please check permissions or set the $ICEAUTHORITY variable manually before" << endl +> << "calling dcop." << endl; +> } +> } +> else +> { +> if( users.count() > 1 ) +> continue; +> else +> { +> cerr << "WARNING: Cannot find ICE authority file " +> << iceFile << "!" << endl +> << "Please check permissions or set the $ICEAUTHORITY" +> << " variable manually before" << endl +> << "calling dcop." << endl; +> } +> } +> } +> +> // Main loop +> // If users is an empty list we're calling for the currently logged +> // in user. In this case we don't have a session, but still want +> // to iterate the loop once. +> QStringList::Iterator sIt = sessions.begin(); +> for( ; sIt != sessions.end() || users.isEmpty(); sIt++ ) +> { +> if( !presetDCOPServer && !users.isEmpty() ) +> { +> QString dcopFile = it.data() + "/" + *sIt; +> QFile f( dcopFile ); +> if( !f.open( IO_ReadOnly ) ) +> { +> cerr << "Can't open " << dcopFile << " for reading!" << endl; +> exit( -1 ); +> } +> +> QStringList l( QStringList::split( '\n', f.readAll() ) ); +> dcopServer = l.first(); +> +> if( dcopServer.isEmpty() ) +> { +> cerr << "WARNING: Unable to determine DCOP server for session " +> << *sIt << "!" << endl +> << "Please check permissions or set the $DCOPSERVER variable manually before" << endl +> << "calling dcop." << endl; +> exit( -1 ); +> } +> } +> +> delete client; +> client = new DCOPClient; +> if( !dcopServer.isEmpty() ) +> client->setServerAddress( dcopServer.ascii() ); +> bool success = client->attach(); +> if( !success ) +> { +> cerr << "ERROR: Couldn't attach to DCOP server!" << endl; +> continue; +> } +> dcop = client; +> +> switch ( args.count() ) +> { +> case 0: +> queryApplications(""); +> break; +> case 1: +> if (endsWith(app, '*')) +> queryApplications(app); +> else +> queryObjects( app, "" ); +> break; +> case 2: +> if (endsWith(objid, '*')) +> queryObjects(app, objid); +> else +> queryFunctions( app, objid ); +> break; +> case 3: +> default: +> if( readStdin ) +> { +> QCStringList::Iterator replaceArg = args.end(); +> +> QCStringList::Iterator it; +> for( it = args.begin(); it != args.end(); it++ ) +> if( *it == "%1" ) +> replaceArg = it; +> +> // Read from stdin until EOF and call function for each line read +> char *buf = new char[ 1000 ]; +> while ( !feof( stdin ) ) +> { +> fgets( buf, 1000, stdin ); +> +> if( replaceArg != args.end() ) +> *replaceArg = buf; +> +> callFunction( app, objid, function, params ); +> } +> } +> else +> { +> // Just call function +> // cout << "call " << app << ", " << objid << ", " << function << ", (params)" << endl; +> callFunction( app, objid, function, params ); +> } +> break; +> } +> // Another sIt++ would make the loop infinite... +> if( users.isEmpty() ) +> break; +> } +> +> // Another it++ would make the loop infinite... +> if( it == users.end() ) +> break; +340a662,767 +> } +> +> +> int main( int argc, char** argv ) +> { +> bool readStdin = false; +> int numOptions = 0; +> QString user; +> Session session = DefaultSession; +> QString sessionName; +> +> // Scan for command-line options first +> for( int pos = 1 ; pos <= argc - 1 ; pos++ ) +> { +> if( strcmp( argv[ pos ], "--help" ) == 0 ) +> showHelp( 0 ); +> else if( strcmp( argv[ pos ], "--pipe" ) == 0 ) +> { +> readStdin = true; +> numOptions++; +> } +> else if( strcmp( argv[ pos ], "--user" ) == 0 ) +> { +> if( pos <= argc - 2 ) +> { +> user = QString::fromLocal8Bit( argv[ pos + 1] ); +> numOptions +=2; +> pos++; +> } +> else +> { +> cerr << "Missing username for '--user' option!" << endl << endl; +> showHelp( -1 ); +> } +> } +> else if( strcmp( argv[ pos ], "--all-users" ) == 0 ) +> { +> user = "*"; +> numOptions ++; +> } +> else if( strcmp( argv[ pos ], "--list-sessions" ) == 0 ) +> { +> session = QuerySessions; +> numOptions ++; +> } +> else if( strcmp( argv[ pos ], "--all-sessions" ) == 0 ) +> { +> session = AllSessions; +> numOptions ++; +> } +> else if( argv[ pos ][ 0 ] == '-' ) +> { +> cerr << "Unknown command-line option '" << argv[ pos ] +> << "'." << endl << endl; +> showHelp( -1 ); +> } +> else +> break; // End of options +> } +> +> argc -= numOptions; +> +> QCStringList args; +> for( int i = numOptions; i < argc + numOptions - 1; i++ ) +> args.append( argv[ i + 1 ] ); +> +> if( readStdin && args.count() < 3 ) +> { +> cerr << "--pipe option only supported for function calls!" << endl << endl; +> showHelp( -1 ); +> } +> +> if( user == "*" && args.count() < 3 && session != QuerySessions ) +> { +> cerr << "ERROR: The --all-users option is only supported for function calls!" << endl << endl; +> showHelp( -1 ); +> } +> +> if( session == QuerySessions && !args.isEmpty() ) +> { +> cerr << "ERROR: The --list-sessions option cannot be used for actual DCOP calls!" << endl << endl; +> showHelp( -1 ); +> } +> +> if( session == QuerySessions && user.isEmpty() ) +> { +> cerr << "ERROR: The --list-sessions option can only be used with the --user or" << endl +> << "--all-users options!" << endl << endl; +> showHelp( -1 ); +> } +> +> if( session != DefaultSession && session != QuerySessions && +> args.count() < 3 ) +> { +> cerr << "ERROR: The --session and --all-sessions options are only supported for function" << endl +> << "calls!" << endl << endl; +> showHelp( -1 ); +> } +> +> UserList users; +> if( user == "*" ) +> users = userList(); +> else if( !user.isEmpty() ) +> users[ user ] = userList()[ user ]; +> +> runDCOP( args, users, session, sessionName, readStdin ); +343a771,773 +> +> // vim: set ts=8 sts=4 sw=4 noet: +> +diff -r dcop/client/dcopfind.cpp dcop2/client/dcopfind.cpp +39c39 +< bool findObject( const char* app, const char* obj, const char* func, int argc, char** args ) +--- +> bool findObject( const char* app, const char* obj, const char* func, QCStringList args ) +121c121 +< if ( (int) types.count() != argc ) { +--- +> if ( types.count() != args.count() ) { +131c131 +< marshall(arg, argc, args, i, *it); +--- +> marshall(arg, args, i, *it); +133c133 +< if ( (int) i != argc ) { +--- +> if ( (uint) i != args.count() ) { +224c224,228 +< findObject( app, objid, function, argc, args ); +--- +> QCStringList params; +> for( int i = 0; i < argc; i++ ) +> params.append( args[ i ] ); +> +> findObject( app, objid, function, params ); +diff -r dcop/client/marshall.cpp dcop2/client/marshall.cpp +245c245 +< void marshall(QDataStream &arg, int argc, char **argv, int &i, QString type) +--- +> void marshall( QDataStream &arg, QCStringList args, uint &i, QString type ) +247,317c247,256 +< if (type == "QStringList") +< type = "QValueList"; +< if (type == "QCStringList") +< type = "QValueList"; +< if (i >= argc) +< { +< qWarning("Not enough arguments."); +< exit(1); +< } +< QString s = QString::fromLocal8Bit(argv[i]); +< +< if ( type == "int" ) +< arg << s.toInt(); +< else if ( type == "uint" ) +< arg << s.toUInt(); +< else if ( type == "unsigned" ) +< arg << s.toUInt(); +< else if ( type == "unsigned int" ) +< arg << s.toUInt(); +< else if ( type == "long" ) +< arg << s.toLong(); +< else if ( type == "long int" ) +< arg << s.toLong(); +< else if ( type == "unsigned long" ) +< arg << s.toULong(); +< else if ( type == "unsigned long int" ) +< arg << s.toULong(); +< else if ( type == "float" ) +< arg << s.toFloat(); +< else if ( type == "double" ) +< arg << s.toDouble(); +< else if ( type == "bool" ) +< arg << mkBool( s ); +< else if ( type == "QString" ) +< arg << s; +< else if ( type == "QCString" ) +< arg << QCString( argv[i] ); +< else if ( type == "QColor" ) +< arg << mkColor( s ); +< else if ( type == "QPoint" ) +< arg << mkPoint( s ); +< else if ( type == "QSize" ) +< arg << mkSize( s ); +< else if ( type == "QRect" ) +< arg << mkRect( s ); +< else if ( type == "QVariant" ) { +< if ( s == "true" || s == "false" ) +< arg << QVariant( mkBool( s ), 42 ); +< else if ( s.left( 4 ) == "int(" ) +< arg << QVariant( s.mid(4, s.length()-5).toInt() ); +< else if ( s.left( 7 ) == "QPoint(" ) +< arg << QVariant( mkPoint( s.mid(7, s.length()-8) ) ); +< else if ( s.left( 6 ) == "QSize(" ) +< arg << QVariant( mkSize( s.mid(6, s.length()-7) ) ); +< else if ( s.left( 6 ) == "QRect(" ) +< arg << QVariant( mkRect( s.mid(6, s.length()-7) ) ); +< else if ( s.left( 7 ) == "QColor(" ) +< arg << QVariant( mkColor( s.mid(7, s.length()-8) ) ); +< else +< arg << QVariant( s ); +< } else if ( type.startsWith("QValueList<")) { +< type = type.mid(11, type.length() - 12); +< QStringList list; +< QString delim = s; +< if (delim == "[") +< delim = "]"; +< if (delim == "(") +< delim = ")"; +< i++; +< QByteArray dummy_data; +< QDataStream dummy_arg(dummy_data, IO_WriteOnly); +--- +> if (type == "QStringList") +> type = "QValueList"; +> if (type == "QCStringList") +> type = "QValueList"; +> if( i > args.count() ) +> { +> qWarning("Not enough arguments."); +> exit(1); +> } +> QString s = QString::fromLocal8Bit( args[ i ] ); +319,346c258,314 +< int j = i; +< int count = 0; +< // Parse list to get the count +< while (true) { +< if (j >= argc) +< { +< qWarning("List end-delimiter '%s' not found.", delim.latin1()); +< exit(1); +< } +< if (argv[j] == delim) break; +< marshall(dummy_arg, argc, argv, j, type); +< count++; +< } +< arg << (Q_UINT32) count; +< // Parse the list for real +< while (true) { +< if (i >= argc) +< { +< qWarning("List end-delimiter '%s' not found.", delim.latin1()); +< exit(1); +< } +< if (argv[i] == delim) break; +< marshall(arg, argc, argv, i, type); +< } +< } else { +< qWarning( "cannot handle datatype '%s'", type.latin1() ); +< exit(1); +< } +--- +> if ( type == "int" ) +> arg << s.toInt(); +> else if ( type == "uint" ) +> arg << s.toUInt(); +> else if ( type == "unsigned" ) +> arg << s.toUInt(); +> else if ( type == "unsigned int" ) +> arg << s.toUInt(); +> else if ( type == "long" ) +> arg << s.toLong(); +> else if ( type == "long int" ) +> arg << s.toLong(); +> else if ( type == "unsigned long" ) +> arg << s.toULong(); +> else if ( type == "unsigned long int" ) +> arg << s.toULong(); +> else if ( type == "float" ) +> arg << s.toFloat(); +> else if ( type == "double" ) +> arg << s.toDouble(); +> else if ( type == "bool" ) +> arg << mkBool( s ); +> else if ( type == "QString" ) +> arg << s; +> else if ( type == "QCString" ) +> arg << QCString( args[ i ] ); +> else if ( type == "QColor" ) +> arg << mkColor( s ); +> else if ( type == "QPoint" ) +> arg << mkPoint( s ); +> else if ( type == "QSize" ) +> arg << mkSize( s ); +> else if ( type == "QRect" ) +> arg << mkRect( s ); +> else if ( type == "QVariant" ) { +> if ( s == "true" || s == "false" ) +> arg << QVariant( mkBool( s ), 42 ); +> else if ( s.left( 4 ) == "int(" ) +> arg << QVariant( s.mid(4, s.length()-5).toInt() ); +> else if ( s.left( 7 ) == "QPoint(" ) +> arg << QVariant( mkPoint( s.mid(7, s.length()-8) ) ); +> else if ( s.left( 6 ) == "QSize(" ) +> arg << QVariant( mkSize( s.mid(6, s.length()-7) ) ); +> else if ( s.left( 6 ) == "QRect(" ) +> arg << QVariant( mkRect( s.mid(6, s.length()-7) ) ); +> else if ( s.left( 7 ) == "QColor(" ) +> arg << QVariant( mkColor( s.mid(7, s.length()-8) ) ); +> else +> arg << QVariant( s ); +> } else if ( type.startsWith("QValueList<")) { +> type = type.mid(11, type.length() - 12); +> QStringList list; +> QString delim = s; +> if (delim == "[") +> delim = "]"; +> if (delim == "(") +> delim = ")"; +347a316,349 +> QByteArray dummy_data; +> QDataStream dummy_arg(dummy_data, IO_WriteOnly); +> +> uint j = i; +> uint count = 0; +> // Parse list to get the count +> while (true) { +> if( j > args.count() ) +> { +> qWarning("List end-delimiter '%s' not found.", delim.latin1()); +> exit(1); +> } +> if( QString::fromLocal8Bit( args[ j ] ) == delim ) +> break; +> marshall( dummy_arg, args, j, type ); +> count++; +> } +> arg << (Q_UINT32) count; +> // Parse the list for real +> while (true) { +> if( i > args.count() ) +> { +> qWarning("List end-delimiter '%s' not found.", delim.latin1()); +> exit(1); +> } +> if( QString::fromLocal8Bit( args[ i ] ) == delim ) +> break; +> marshall( arg, args, i, type ); +> } +> } else { +> qWarning( "cannot handle datatype '%s'", type.latin1() ); +> exit(1); +> } +> i++; diff --git a/kompare/tests/diff/rcs.diff b/kompare/tests/diff/rcs.diff new file mode 100644 index 00000000..08069790 --- /dev/null +++ b/kompare/tests/diff/rcs.diff @@ -0,0 +1,9 @@ +d1 2 +d4 1 +a4 2 +The named is the mother of all things. + +a11 3 +They both may be called deep and profound. +Deeper and more profound, +The door of all subtleties! diff --git a/kompare/tests/diff/rcsm.diff b/kompare/tests/diff/rcsm.diff new file mode 100644 index 00000000..0c4222f9 --- /dev/null +++ b/kompare/tests/diff/rcsm.diff @@ -0,0 +1,671 @@ +diff -nr dcop/client/dcop.cpp dcop2/client/dcop.cpp +d23 1 +a23 4 +#include +#include +#include + +d25 1 +a25 12 +#include +#include +#include +#include +#include +#include +#include + +// putenv() is not available on all platforms, so make sure the emulation +// wrapper is available in those cases by loading config.h! +#include + +d28 3 +a30 1 +#include "../kdatastream.h" +a33 2 +typedef QMap UserList; + +a35 14 +static QTextStream cout( stdout, IO_WriteOnly ); +static QTextStream cerr( stderr, IO_WriteOnly ); + +/** + * Session to send call to + * DefaultSession - current session. Current KDE session when called without + * --user or --all-users option. Otherwise this value ignores + * all users with more than one active session. + * AllSessions - Send to all sessions found. requires --user or --all-users. + * QuerySessions - Don't call DCOP, return a list of available sessions. + * CustomSession - Use the specified session + */ +enum Session { DefaultSession = 0, AllSessions, QuerySessions, CustomSession }; + +d121 1 +a121 1 +void callFunction( const char* app, const char* obj, const char* func, const QCStringList args ) +d123 1 +d139 1 +a139 1 + if ( !ok && args.isEmpty() ) +d156 2 +a157 2 + uint a = (*it).contains(','); + if ( ( a == 0 && args.isEmpty() ) || ( a > 0 && a + 1 == args.count() ) ) +d164 1 +a164 2 +// exit(1); + return; +d246 5 +a250 6 + uint i = 0; + for( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) + marshall( arg, args, i, *it ); + + if ( i != args.count() ) + { +a268 27 +/** + * Show command-line help and exit + */ +void showHelp( int exitCode = 0 ) +{ + cout << "Usage: dcop [options] [application [object [function [arg1] [arg2] ... ] ] ]" << endl + << "" << endl + << "Console DCOP client" << endl + << "" << endl + << "Generic options:" << endl + << " --help Show help about options" << endl + << "" << endl + << "Options:" << endl + << " --pipe Call DCOP for each line read from stdin" << endl + << " --user Connect to the given user's DCOP server. This option will" << endl + << " ignore the values of the environment vars $DCOPSERVER and" << endl + << " $ICEAUTHORITY, even if they are set." << endl + << " If the user has more than one open session, you must also" << endl + << " use one of the --list-sessions, --session or --als-sessions" << endl + << " command-line options." << endl + << " --all-users Send the same DCOP call to all users with a running DCOP" << endl + << " server. Only failed calls to existing DCOP servers will" + << " generate an error message. If no DCOP server is available" << endl + << " at all, no error will be generated." << endl; + + exit( exitCode ); +} +d270 2 +a271 5 +/** + * Return a list of all users and their home directories. + * Returns an empty list if /etc/passwd cannot be read for some reason. + */ +static UserList userList() +a272 9 + UserList result; + + QFile f( "/etc/passwd" ); + + if( !f.open( IO_ReadOnly ) ) + { + cerr << "Can't open /etc/passwd for reading!" << endl; + return result; + } +d274 3 +a276 6 + QStringList l( QStringList::split( '\n', f.readAll() ) ); + + for( QStringList::ConstIterator it( l.begin() ); it != l.end(); ++it ) + { + QStringList userInfo( QStringList::split( ':', *it, true ) ); + result[ userInfo[ 0 ] ] = userInfo[ 5 ]; +d279 3 +a281 42 + return result; +} + +/** + * Return a list of available DCOP sessions for the specified user + * An empty list means no sessions are available, or an error occurred. + */ +QStringList dcopSessionList( const QString &user, const QString &home ) +{ + if( home.isEmpty() ) + { + cerr << "WARNING: Cannot determine home directory for user " + << user << "!" << endl + << "Please check permissions or set the $DCOPSERVER variable manually before" << endl + << "calling dcop." << endl; + return QStringList(); + } + + QStringList result; + QFileInfo dirInfo( home ); + if( !dirInfo.exists() || !dirInfo.isReadable() ) + return result; + + QDir d( home ); + d.setFilter( QDir::Files | QDir::Hidden | QDir::NoSymLinks ); + d.setNameFilter( ".DCOPserver*" ); + + const QFileInfoList *list = d.entryInfoList(); + if( !list ) + return result; + + QFileInfoListIterator it( *list ); + QFileInfo *fi; + + while ( ( fi = it.current() ) != 0 ) + { + if( fi->isReadable() ) + result.append( fi->fileName() ); + ++it; + } + return result; +} +a282 6 +/** + * Do the actual DCOP call + */ +void runDCOP( QCStringList args, UserList users, Session session, + const QString sessionName, bool readStdin ) +{ +d286 2 +a287 3 + QCStringList params; + DCOPClient *client = 0L; + if ( !args.isEmpty() && args[ 0 ].find( "DCOPRef(" ) == 0 ) +d289 16 +a304 24 + // WARNING: This part (until the closing '}') could very + // well be broken now. As I don't know how to trigger and test + // dcoprefs this code is *not* tested. It compiles and it looks + // ok to me, but that's all I can say - Martijn (2001/12/24) + int delimPos = args[ 0 ].findRev( ',' ); + if( delimPos == -1 ) + { + cerr << "Error: '" << args[ 0 ] + << "' is not a valid DCOP reference." << endl; + exit( -1 ); + } + args[ 0 ][ delimPos ] = 0; + app = args[ 0 ].mid( 8 ); + delimPos++; + args[ 0 ][ args[ 0 ].length() - 1 ] = 0; + objid = args[ 0 ].mid( delimPos ); + if( args.count() > 1 ) + function = args[ 1 ]; + if( args.count() > 2 ) + { + params = args; + params.remove( params.begin() ); + params.remove( params.begin() ); + } +d308 31 +a338 84 + if( !args.isEmpty() ) + app = args[ 0 ]; + if( args.count() > 1 ) + objid = args[ 1 ]; + if( args.count() > 2 ) + function = args[ 2 ]; + if( args.count() > 3) + { + params = args; + params.remove( params.begin() ); + params.remove( params.begin() ); + params.remove( params.begin() ); + } + } + + bool firstRun = true; + UserList::Iterator it; + QStringList sessions; + bool presetDCOPServer = false; +// char *dcopStr = 0L; + QString dcopServer; + + for( it = users.begin(); it != users.end() || firstRun; it++ ) + { + firstRun = false; + + //cout << "Iterating '" << it.key() << "'" << endl; + + if( session == QuerySessions ) + { + QStringList sessions = dcopSessionList( it.key(), it.data() ); + if( sessions.isEmpty() ) + { + cout << "No active sessions"; + if( !( *it ).isEmpty() ) + cout << " for user " << *it; + cout << endl; + } + else + { + cout << "Active sessions "; + if( !( *it ).isEmpty() ) + cout << "for user " << *it << " "; + cout << ":" << endl; + + QStringList::Iterator sIt; + for( sIt = sessions.begin(); sIt != sessions.end(); sIt++ ) + cout << " " << *sIt << endl; + + cout << endl; + } + continue; + } + + if( getenv( "DCOPSERVER" ) ) + { + sessions.append( getenv( "DCOPSERVER" ) ); + presetDCOPServer = true; + } + + if( users.count() > 1 || ( users.count() == 1 && + ( getenv( "DCOPSERVER" ) == 0 /*&& getenv( "DISPLAY" ) == 0*/ ) ) ) + { + sessions = dcopSessionList( it.key(), it.data() ); + if( sessions.isEmpty() ) + { + if( users.count() > 1 ) + continue; + else + { + cerr << "ERROR: No active KDE sessions!" << endl + << "If you are sure there is one, please set the $DCOPSERVER variable manually" << endl + << "before calling dcop." << endl; + exit( -1 ); + } + } + else if( sessions.count() > 1 && session != AllSessions ) + { + cerr << "ERROR: Multiple available KDE sessions!" << endl + << "Please specify the correct session to use with --session or use the" << endl + << "--all-sessions option to broadcast to all sessions." << endl; + exit( -1 ); + } + } +a339 143 + if( users.count() > 1 || ( users.count() == 1 && + ( getenv( "ICEAUTHORITY" ) == 0 || getenv( "DISPLAY" ) == 0 ) ) ) + { + // Check for ICE authority file and if the file can be read by us + QString home = it.data(); + QString iceFile = it.data() + "/.ICEauthority"; + QFileInfo fi( iceFile ); + if( iceFile.isEmpty() ) + { + cerr << "WARNING: Cannot determine home directory for user " + << it.key() << "!" << endl + << "Please check permissions or set the $ICEAUTHORITY variable manually before" << endl + << "calling dcop." << endl; + } + else if( fi.exists() ) + { + if( fi.isReadable() ) + { + char *envStr = strdup( ( "ICEAUTHORITY=" + iceFile ).ascii() ); + putenv( envStr ); + //cerr << "ice: " << envStr << endl; + } + else + { + cerr << "WARNING: ICE authority file " << iceFile + << "is not readable by you!" << endl + << "Please check permissions or set the $ICEAUTHORITY variable manually before" << endl + << "calling dcop." << endl; + } + } + else + { + if( users.count() > 1 ) + continue; + else + { + cerr << "WARNING: Cannot find ICE authority file " + << iceFile << "!" << endl + << "Please check permissions or set the $ICEAUTHORITY" + << " variable manually before" << endl + << "calling dcop." << endl; + } + } + } + + // Main loop + // If users is an empty list we're calling for the currently logged + // in user. In this case we don't have a session, but still want + // to iterate the loop once. + QStringList::Iterator sIt = sessions.begin(); + for( ; sIt != sessions.end() || users.isEmpty(); sIt++ ) + { + if( !presetDCOPServer && !users.isEmpty() ) + { + QString dcopFile = it.data() + "/" + *sIt; + QFile f( dcopFile ); + if( !f.open( IO_ReadOnly ) ) + { + cerr << "Can't open " << dcopFile << " for reading!" << endl; + exit( -1 ); + } + + QStringList l( QStringList::split( '\n', f.readAll() ) ); + dcopServer = l.first(); + + if( dcopServer.isEmpty() ) + { + cerr << "WARNING: Unable to determine DCOP server for session " + << *sIt << "!" << endl + << "Please check permissions or set the $DCOPSERVER variable manually before" << endl + << "calling dcop." << endl; + exit( -1 ); + } + } + + delete client; + client = new DCOPClient; + if( !dcopServer.isEmpty() ) + client->setServerAddress( dcopServer.ascii() ); + bool success = client->attach(); + if( !success ) + { + cerr << "ERROR: Couldn't attach to DCOP server!" << endl; + continue; + } + dcop = client; + + switch ( args.count() ) + { + case 0: + queryApplications(""); + break; + case 1: + if (endsWith(app, '*')) + queryApplications(app); + else + queryObjects( app, "" ); + break; + case 2: + if (endsWith(objid, '*')) + queryObjects(app, objid); + else + queryFunctions( app, objid ); + break; + case 3: + default: + if( readStdin ) + { + QCStringList::Iterator replaceArg = args.end(); + + QCStringList::Iterator it; + for( it = args.begin(); it != args.end(); it++ ) + if( *it == "%1" ) + replaceArg = it; + + // Read from stdin until EOF and call function for each line read + char *buf = new char[ 1000 ]; + while ( !feof( stdin ) ) + { + fgets( buf, 1000, stdin ); + + if( replaceArg != args.end() ) + *replaceArg = buf; + + callFunction( app, objid, function, params ); + } + } + else + { + // Just call function +// cout << "call " << app << ", " << objid << ", " << function << ", (params)" << endl; + callFunction( app, objid, function, params ); + } + break; + } + // Another sIt++ would make the loop infinite... + if( users.isEmpty() ) + break; + } + + // Another it++ would make the loop infinite... + if( it == users.end() ) + break; +a340 106 +} + + +int main( int argc, char** argv ) +{ + bool readStdin = false; + int numOptions = 0; + QString user; + Session session = DefaultSession; + QString sessionName; + + // Scan for command-line options first + for( int pos = 1 ; pos <= argc - 1 ; pos++ ) + { + if( strcmp( argv[ pos ], "--help" ) == 0 ) + showHelp( 0 ); + else if( strcmp( argv[ pos ], "--pipe" ) == 0 ) + { + readStdin = true; + numOptions++; + } + else if( strcmp( argv[ pos ], "--user" ) == 0 ) + { + if( pos <= argc - 2 ) + { + user = QString::fromLocal8Bit( argv[ pos + 1] ); + numOptions +=2; + pos++; + } + else + { + cerr << "Missing username for '--user' option!" << endl << endl; + showHelp( -1 ); + } + } + else if( strcmp( argv[ pos ], "--all-users" ) == 0 ) + { + user = "*"; + numOptions ++; + } + else if( strcmp( argv[ pos ], "--list-sessions" ) == 0 ) + { + session = QuerySessions; + numOptions ++; + } + else if( strcmp( argv[ pos ], "--all-sessions" ) == 0 ) + { + session = AllSessions; + numOptions ++; + } + else if( argv[ pos ][ 0 ] == '-' ) + { + cerr << "Unknown command-line option '" << argv[ pos ] + << "'." << endl << endl; + showHelp( -1 ); + } + else + break; // End of options + } + + argc -= numOptions; + + QCStringList args; + for( int i = numOptions; i < argc + numOptions - 1; i++ ) + args.append( argv[ i + 1 ] ); + + if( readStdin && args.count() < 3 ) + { + cerr << "--pipe option only supported for function calls!" << endl << endl; + showHelp( -1 ); + } + + if( user == "*" && args.count() < 3 && session != QuerySessions ) + { + cerr << "ERROR: The --all-users option is only supported for function calls!" << endl << endl; + showHelp( -1 ); + } + + if( session == QuerySessions && !args.isEmpty() ) + { + cerr << "ERROR: The --list-sessions option cannot be used for actual DCOP calls!" << endl << endl; + showHelp( -1 ); + } + + if( session == QuerySessions && user.isEmpty() ) + { + cerr << "ERROR: The --list-sessions option can only be used with the --user or" << endl + << "--all-users options!" << endl << endl; + showHelp( -1 ); + } + + if( session != DefaultSession && session != QuerySessions && + args.count() < 3 ) + { + cerr << "ERROR: The --session and --all-sessions options are only supported for function" << endl + << "calls!" << endl << endl; + showHelp( -1 ); + } + + UserList users; + if( user == "*" ) + users = userList(); + else if( !user.isEmpty() ) + users[ user ] = userList()[ user ]; + + runDCOP( args, users, session, sessionName, readStdin ); +a343 3 + +// vim: set ts=8 sts=4 sw=4 noet: + +diff -nr dcop/client/dcopfind.cpp dcop2/client/dcopfind.cpp +d39 1 +a39 1 +bool findObject( const char* app, const char* obj, const char* func, QCStringList args ) +d121 1 +a121 1 + if ( types.count() != args.count() ) { +d131 1 +a131 1 + marshall(arg, args, i, *it); +d133 1 +a133 1 + if ( (uint) i != args.count() ) { +d224 1 +a224 5 + QCStringList params; + for( int i = 0; i < argc; i++ ) + params.append( args[ i ] ); + + findObject( app, objid, function, params ); +diff -nr dcop/client/marshall.cpp dcop2/client/marshall.cpp +d245 1 +a245 1 +void marshall( QDataStream &arg, QCStringList args, uint &i, QString type ) +d247 71 +a317 10 + if (type == "QStringList") + type = "QValueList"; + if (type == "QCStringList") + type = "QValueList"; + if( i > args.count() ) + { + qWarning("Not enough arguments."); + exit(1); + } + QString s = QString::fromLocal8Bit( args[ i ] ); +d319 28 +a346 57 + if ( type == "int" ) + arg << s.toInt(); + else if ( type == "uint" ) + arg << s.toUInt(); + else if ( type == "unsigned" ) + arg << s.toUInt(); + else if ( type == "unsigned int" ) + arg << s.toUInt(); + else if ( type == "long" ) + arg << s.toLong(); + else if ( type == "long int" ) + arg << s.toLong(); + else if ( type == "unsigned long" ) + arg << s.toULong(); + else if ( type == "unsigned long int" ) + arg << s.toULong(); + else if ( type == "float" ) + arg << s.toFloat(); + else if ( type == "double" ) + arg << s.toDouble(); + else if ( type == "bool" ) + arg << mkBool( s ); + else if ( type == "QString" ) + arg << s; + else if ( type == "QCString" ) + arg << QCString( args[ i ] ); + else if ( type == "QColor" ) + arg << mkColor( s ); + else if ( type == "QPoint" ) + arg << mkPoint( s ); + else if ( type == "QSize" ) + arg << mkSize( s ); + else if ( type == "QRect" ) + arg << mkRect( s ); + else if ( type == "QVariant" ) { + if ( s == "true" || s == "false" ) + arg << QVariant( mkBool( s ), 42 ); + else if ( s.left( 4 ) == "int(" ) + arg << QVariant( s.mid(4, s.length()-5).toInt() ); + else if ( s.left( 7 ) == "QPoint(" ) + arg << QVariant( mkPoint( s.mid(7, s.length()-8) ) ); + else if ( s.left( 6 ) == "QSize(" ) + arg << QVariant( mkSize( s.mid(6, s.length()-7) ) ); + else if ( s.left( 6 ) == "QRect(" ) + arg << QVariant( mkRect( s.mid(6, s.length()-7) ) ); + else if ( s.left( 7 ) == "QColor(" ) + arg << QVariant( mkColor( s.mid(7, s.length()-8) ) ); + else + arg << QVariant( s ); + } else if ( type.startsWith("QValueList<")) { + type = type.mid(11, type.length() - 12); + QStringList list; + QString delim = s; + if (delim == "[") + delim = "]"; + if (delim == "(") + delim = ")"; +a347 34 + QByteArray dummy_data; + QDataStream dummy_arg(dummy_data, IO_WriteOnly); + + uint j = i; + uint count = 0; + // Parse list to get the count + while (true) { + if( j > args.count() ) + { + qWarning("List end-delimiter '%s' not found.", delim.latin1()); + exit(1); + } + if( QString::fromLocal8Bit( args[ j ] ) == delim ) + break; + marshall( dummy_arg, args, j, type ); + count++; + } + arg << (Q_UINT32) count; + // Parse the list for real + while (true) { + if( i > args.count() ) + { + qWarning("List end-delimiter '%s' not found.", delim.latin1()); + exit(1); + } + if( QString::fromLocal8Bit( args[ i ] ) == delim ) + break; + marshall( arg, args, i, type ); + } + } else { + qWarning( "cannot handle datatype '%s'", type.latin1() ); + exit(1); + } + i++; diff --git a/kompare/tests/diff/unified.diff b/kompare/tests/diff/unified.diff new file mode 100644 index 00000000..952e648c --- /dev/null +++ b/kompare/tests/diff/unified.diff @@ -0,0 +1,19 @@ +--- /home/John/lao Thu Apr 12 11:09:30 2001 ++++ /home/John/tzu Sat Jul 28 13:23:25 2001 +@@ -1,7 +1,6 @@ +-The Way that can be told of is not the eternal Way; +-The name that can be named is not the eternal name. + The Nameless is the origin of Heaven and Earth; +-The Named is the mother of all things. ++The named is the mother of all things. ++ + Therefore let there always be non-being, + so we may see their subtlety, + And let there always be being, +@@ -9,3 +8,6 @@ + The two are the same, + But after they are produced, + they have different names. ++They both may be called deep and profound. ++Deeper and more profound, ++The door of all subtleties! diff --git a/kompare/tests/diff/unifiedm.diff b/kompare/tests/diff/unifiedm.diff new file mode 100644 index 00000000..4a30c6b4 --- /dev/null +++ b/kompare/tests/diff/unifiedm.diff @@ -0,0 +1,911 @@ +diff -aur dcop/client/dcop.cpp dcop2/client/dcop.cpp +--- dcop/client/dcop.cpp Wed Jan 30 22:38:07 2002 ++++ dcop2/client/dcop.cpp Wed Jan 30 22:37:04 2002 +@@ -20,19 +20,47 @@ + + ******************************************************************/ + +-#include ++#include ++#include ++#include ++ + #include +-#include "../kdatastream.h" ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++// putenv() is not available on all platforms, so make sure the emulation ++// wrapper is available in those cases by loading config.h! ++#include ++ + #include "../dcopclient.h" + #include "../dcopref.h" +-#include +-#include +-#include ++#include "../kdatastream.h" + + #include "marshall.cpp" + ++typedef QMap UserList; ++ + static DCOPClient* dcop = 0; + ++static QTextStream cout( stdout, IO_WriteOnly ); ++static QTextStream cerr( stderr, IO_WriteOnly ); ++ ++/** ++ * Session to send call to ++ * DefaultSession - current session. Current KDE session when called without ++ * --user or --all-users option. Otherwise this value ignores ++ * all users with more than one active session. ++ * AllSessions - Send to all sessions found. requires --user or --all-users. ++ * QuerySessions - Don't call DCOP, return a list of available sessions. ++ * CustomSession - Use the specified session ++ */ ++enum Session { DefaultSession = 0, AllSessions, QuerySessions, CustomSession }; ++ + bool startsWith(const QCString &id, const char *str, int n) + { + return !n || (strncmp(id.data(), str, n) == 0); +@@ -118,9 +146,8 @@ + } + } + +-void callFunction( const char* app, const char* obj, const char* func, int argc, char** args ) ++void callFunction( const char* app, const char* obj, const char* func, const QCStringList args ) + { +- + QString f = func; // Qt is better with unicode strings, so use one. + int left = f.find( '(' ); + int right = f.find( ')' ); +@@ -136,7 +163,7 @@ + bool ok = false; + QCStringList funcs = dcop->remoteFunctions( app, obj, &ok ); + QCString realfunc; +- if ( !ok && argc == 0 ) ++ if ( !ok && args.isEmpty() ) + goto doit; + if ( !ok ) + { +@@ -153,15 +180,16 @@ + + if ( l > 0 && (*it).mid( s, l - s ) == func ) { + realfunc = (*it).mid( s ); +- int a = (*it).contains(','); +- if ( ( a == 0 && argc == 0) || ( a > 0 && a + 1 == argc ) ) ++ uint a = (*it).contains(','); ++ if ( ( a == 0 && args.isEmpty() ) || ( a > 0 && a + 1 == args.count() ) ) + break; + } + } + if ( realfunc.isEmpty() ) + { + qWarning("no such function"); +- exit(1); ++// exit(1); ++ return; + } + f = realfunc; + left = f.find( '(' ); +@@ -243,11 +271,12 @@ + QCString replyType; + QDataStream arg(data, IO_WriteOnly); + +- int i = 0; +- for ( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) { +- marshall(arg, argc, args, i, *it); +- } +- if ( i != argc ) { ++ uint i = 0; ++ for( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) ++ marshall( arg, args, i, *it ); ++ ++ if ( i != args.count() ) ++ { + qWarning( "arguments do not match" ); + exit(1); + } +@@ -266,78 +295,479 @@ + } + } + ++/** ++ * Show command-line help and exit ++ */ ++void showHelp( int exitCode = 0 ) ++{ ++ cout << "Usage: dcop [options] [application [object [function [arg1] [arg2] ... ] ] ]" << endl ++ << "" << endl ++ << "Console DCOP client" << endl ++ << "" << endl ++ << "Generic options:" << endl ++ << " --help Show help about options" << endl ++ << "" << endl ++ << "Options:" << endl ++ << " --pipe Call DCOP for each line read from stdin" << endl ++ << " --user Connect to the given user's DCOP server. This option will" << endl ++ << " ignore the values of the environment vars $DCOPSERVER and" << endl ++ << " $ICEAUTHORITY, even if they are set." << endl ++ << " If the user has more than one open session, you must also" << endl ++ << " use one of the --list-sessions, --session or --als-sessions" << endl ++ << " command-line options." << endl ++ << " --all-users Send the same DCOP call to all users with a running DCOP" << endl ++ << " server. Only failed calls to existing DCOP servers will" ++ << " generate an error message. If no DCOP server is available" << endl ++ << " at all, no error will be generated." << endl; ++ ++ exit( exitCode ); ++} + +- +-int main( int argc, char** argv ) ++/** ++ * Return a list of all users and their home directories. ++ * Returns an empty list if /etc/passwd cannot be read for some reason. ++ */ ++static UserList userList() + { ++ UserList result; ++ ++ QFile f( "/etc/passwd" ); ++ ++ if( !f.open( IO_ReadOnly ) ) ++ { ++ cerr << "Can't open /etc/passwd for reading!" << endl; ++ return result; ++ } + +- if ( argc > 1 && argv[1][0] == '-' ) { +- fprintf( stderr, "Usage: dcop [ application [object [function [arg1] [arg2] [arg3] ... ] ] ] \n" ); +- exit(0); ++ QStringList l( QStringList::split( '\n', f.readAll() ) ); ++ ++ for( QStringList::ConstIterator it( l.begin() ); it != l.end(); ++it ) ++ { ++ QStringList userInfo( QStringList::split( ':', *it, true ) ); ++ result[ userInfo[ 0 ] ] = userInfo[ 5 ]; + } + +- DCOPClient client; +- client.attach(); +- dcop = &client; ++ return result; ++} ++ ++/** ++ * Return a list of available DCOP sessions for the specified user ++ * An empty list means no sessions are available, or an error occurred. ++ */ ++QStringList dcopSessionList( const QString &user, const QString &home ) ++{ ++ if( home.isEmpty() ) ++ { ++ cerr << "WARNING: Cannot determine home directory for user " ++ << user << "!" << endl ++ << "Please check permissions or set the $DCOPSERVER variable manually before" << endl ++ << "calling dcop." << endl; ++ return QStringList(); ++ } ++ ++ QStringList result; ++ QFileInfo dirInfo( home ); ++ if( !dirInfo.exists() || !dirInfo.isReadable() ) ++ return result; ++ ++ QDir d( home ); ++ d.setFilter( QDir::Files | QDir::Hidden | QDir::NoSymLinks ); ++ d.setNameFilter( ".DCOPserver*" ); ++ ++ const QFileInfoList *list = d.entryInfoList(); ++ if( !list ) ++ return result; ++ ++ QFileInfoListIterator it( *list ); ++ QFileInfo *fi; ++ ++ while ( ( fi = it.current() ) != 0 ) ++ { ++ if( fi->isReadable() ) ++ result.append( fi->fileName() ); ++ ++it; ++ } ++ return result; ++} + ++/** ++ * Do the actual DCOP call ++ */ ++void runDCOP( QCStringList args, UserList users, Session session, ++ const QString sessionName, bool readStdin ) ++{ + QCString app; + QCString objid; + QCString function; +- char **args = 0; +- if ((argc > 1) && (strncmp(argv[1], "DCOPRef(", 8)) == 0) ++ QCStringList params; ++ DCOPClient *client = 0L; ++ if ( !args.isEmpty() && args[ 0 ].find( "DCOPRef(" ) == 0 ) + { +- char *delim = strchr(argv[1], ','); +- if (!delim) +- { +- fprintf(stderr, "Error: '%s' is not a valid DCOP reference.\n", argv[1]); +- return 1; +- } +- *delim = 0; +- app = argv[1] + 8; +- delim++; +- delim[strlen(delim)-1] = 0; +- objid = delim; +- if (argc > 2) +- function = argv[2]; +- if (argc > 3) +- args = &argv[3]; +- argc++; ++ // WARNING: This part (until the closing '}') could very ++ // well be broken now. As I don't know how to trigger and test ++ // dcoprefs this code is *not* tested. It compiles and it looks ++ // ok to me, but that's all I can say - Martijn (2001/12/24) ++ int delimPos = args[ 0 ].findRev( ',' ); ++ if( delimPos == -1 ) ++ { ++ cerr << "Error: '" << args[ 0 ] ++ << "' is not a valid DCOP reference." << endl; ++ exit( -1 ); ++ } ++ args[ 0 ][ delimPos ] = 0; ++ app = args[ 0 ].mid( 8 ); ++ delimPos++; ++ args[ 0 ][ args[ 0 ].length() - 1 ] = 0; ++ objid = args[ 0 ].mid( delimPos ); ++ if( args.count() > 1 ) ++ function = args[ 1 ]; ++ if( args.count() > 2 ) ++ { ++ params = args; ++ params.remove( params.begin() ); ++ params.remove( params.begin() ); ++ } + } + else + { +- if (argc > 1) +- app = argv[1]; +- if (argc > 2) +- objid = argv[2]; +- if (argc > 3) +- function = argv[3]; +- if (argc > 4) +- args = &argv[4]; +- } +- +- switch ( argc ) { +- case 0: +- case 1: +- queryApplications(""); +- break; +- case 2: +- if (endsWith(app, '*')) +- queryApplications(app); +- else +- queryObjects( app, "" ); +- break; +- case 3: +- if (endsWith(objid, '*')) +- queryObjects(app, objid); +- else +- queryFunctions( app, objid ); +- break; +- case 4: +- default: +- callFunction( app, objid, function, argc - 4, args ); +- break; ++ if( !args.isEmpty() ) ++ app = args[ 0 ]; ++ if( args.count() > 1 ) ++ objid = args[ 1 ]; ++ if( args.count() > 2 ) ++ function = args[ 2 ]; ++ if( args.count() > 3) ++ { ++ params = args; ++ params.remove( params.begin() ); ++ params.remove( params.begin() ); ++ params.remove( params.begin() ); ++ } ++ } ++ ++ bool firstRun = true; ++ UserList::Iterator it; ++ QStringList sessions; ++ bool presetDCOPServer = false; ++// char *dcopStr = 0L; ++ QString dcopServer; ++ ++ for( it = users.begin(); it != users.end() || firstRun; it++ ) ++ { ++ firstRun = false; ++ ++ //cout << "Iterating '" << it.key() << "'" << endl; ++ ++ if( session == QuerySessions ) ++ { ++ QStringList sessions = dcopSessionList( it.key(), it.data() ); ++ if( sessions.isEmpty() ) ++ { ++ cout << "No active sessions"; ++ if( !( *it ).isEmpty() ) ++ cout << " for user " << *it; ++ cout << endl; ++ } ++ else ++ { ++ cout << "Active sessions "; ++ if( !( *it ).isEmpty() ) ++ cout << "for user " << *it << " "; ++ cout << ":" << endl; ++ ++ QStringList::Iterator sIt; ++ for( sIt = sessions.begin(); sIt != sessions.end(); sIt++ ) ++ cout << " " << *sIt << endl; ++ ++ cout << endl; ++ } ++ continue; ++ } ++ ++ if( getenv( "DCOPSERVER" ) ) ++ { ++ sessions.append( getenv( "DCOPSERVER" ) ); ++ presetDCOPServer = true; ++ } ++ ++ if( users.count() > 1 || ( users.count() == 1 && ++ ( getenv( "DCOPSERVER" ) == 0 /*&& getenv( "DISPLAY" ) == 0*/ ) ) ) ++ { ++ sessions = dcopSessionList( it.key(), it.data() ); ++ if( sessions.isEmpty() ) ++ { ++ if( users.count() > 1 ) ++ continue; ++ else ++ { ++ cerr << "ERROR: No active KDE sessions!" << endl ++ << "If you are sure there is one, please set the $DCOPSERVER variable manually" << endl ++ << "before calling dcop." << endl; ++ exit( -1 ); ++ } ++ } ++ else if( sessions.count() > 1 && session != AllSessions ) ++ { ++ cerr << "ERROR: Multiple available KDE sessions!" << endl ++ << "Please specify the correct session to use with --session or use the" << endl ++ << "--all-sessions option to broadcast to all sessions." << endl; ++ exit( -1 ); ++ } ++ } + ++ if( users.count() > 1 || ( users.count() == 1 && ++ ( getenv( "ICEAUTHORITY" ) == 0 || getenv( "DISPLAY" ) == 0 ) ) ) ++ { ++ // Check for ICE authority file and if the file can be read by us ++ QString home = it.data(); ++ QString iceFile = it.data() + "/.ICEauthority"; ++ QFileInfo fi( iceFile ); ++ if( iceFile.isEmpty() ) ++ { ++ cerr << "WARNING: Cannot determine home directory for user " ++ << it.key() << "!" << endl ++ << "Please check permissions or set the $ICEAUTHORITY variable manually before" << endl ++ << "calling dcop." << endl; ++ } ++ else if( fi.exists() ) ++ { ++ if( fi.isReadable() ) ++ { ++ char *envStr = strdup( ( "ICEAUTHORITY=" + iceFile ).ascii() ); ++ putenv( envStr ); ++ //cerr << "ice: " << envStr << endl; ++ } ++ else ++ { ++ cerr << "WARNING: ICE authority file " << iceFile ++ << "is not readable by you!" << endl ++ << "Please check permissions or set the $ICEAUTHORITY variable manually before" << endl ++ << "calling dcop." << endl; ++ } ++ } ++ else ++ { ++ if( users.count() > 1 ) ++ continue; ++ else ++ { ++ cerr << "WARNING: Cannot find ICE authority file " ++ << iceFile << "!" << endl ++ << "Please check permissions or set the $ICEAUTHORITY" ++ << " variable manually before" << endl ++ << "calling dcop." << endl; ++ } ++ } ++ } ++ ++ // Main loop ++ // If users is an empty list we're calling for the currently logged ++ // in user. In this case we don't have a session, but still want ++ // to iterate the loop once. ++ QStringList::Iterator sIt = sessions.begin(); ++ for( ; sIt != sessions.end() || users.isEmpty(); sIt++ ) ++ { ++ if( !presetDCOPServer && !users.isEmpty() ) ++ { ++ QString dcopFile = it.data() + "/" + *sIt; ++ QFile f( dcopFile ); ++ if( !f.open( IO_ReadOnly ) ) ++ { ++ cerr << "Can't open " << dcopFile << " for reading!" << endl; ++ exit( -1 ); ++ } ++ ++ QStringList l( QStringList::split( '\n', f.readAll() ) ); ++ dcopServer = l.first(); ++ ++ if( dcopServer.isEmpty() ) ++ { ++ cerr << "WARNING: Unable to determine DCOP server for session " ++ << *sIt << "!" << endl ++ << "Please check permissions or set the $DCOPSERVER variable manually before" << endl ++ << "calling dcop." << endl; ++ exit( -1 ); ++ } ++ } ++ ++ delete client; ++ client = new DCOPClient; ++ if( !dcopServer.isEmpty() ) ++ client->setServerAddress( dcopServer.ascii() ); ++ bool success = client->attach(); ++ if( !success ) ++ { ++ cerr << "ERROR: Couldn't attach to DCOP server!" << endl; ++ continue; ++ } ++ dcop = client; ++ ++ switch ( args.count() ) ++ { ++ case 0: ++ queryApplications(""); ++ break; ++ case 1: ++ if (endsWith(app, '*')) ++ queryApplications(app); ++ else ++ queryObjects( app, "" ); ++ break; ++ case 2: ++ if (endsWith(objid, '*')) ++ queryObjects(app, objid); ++ else ++ queryFunctions( app, objid ); ++ break; ++ case 3: ++ default: ++ if( readStdin ) ++ { ++ QCStringList::Iterator replaceArg = args.end(); ++ ++ QCStringList::Iterator it; ++ for( it = args.begin(); it != args.end(); it++ ) ++ if( *it == "%1" ) ++ replaceArg = it; ++ ++ // Read from stdin until EOF and call function for each line read ++ char *buf = new char[ 1000 ]; ++ while ( !feof( stdin ) ) ++ { ++ fgets( buf, 1000, stdin ); ++ ++ if( replaceArg != args.end() ) ++ *replaceArg = buf; ++ ++ callFunction( app, objid, function, params ); ++ } ++ } ++ else ++ { ++ // Just call function ++// cout << "call " << app << ", " << objid << ", " << function << ", (params)" << endl; ++ callFunction( app, objid, function, params ); ++ } ++ break; ++ } ++ // Another sIt++ would make the loop infinite... ++ if( users.isEmpty() ) ++ break; ++ } ++ ++ // Another it++ would make the loop infinite... ++ if( it == users.end() ) ++ break; + } ++} ++ ++ ++int main( int argc, char** argv ) ++{ ++ bool readStdin = false; ++ int numOptions = 0; ++ QString user; ++ Session session = DefaultSession; ++ QString sessionName; ++ ++ // Scan for command-line options first ++ for( int pos = 1 ; pos <= argc - 1 ; pos++ ) ++ { ++ if( strcmp( argv[ pos ], "--help" ) == 0 ) ++ showHelp( 0 ); ++ else if( strcmp( argv[ pos ], "--pipe" ) == 0 ) ++ { ++ readStdin = true; ++ numOptions++; ++ } ++ else if( strcmp( argv[ pos ], "--user" ) == 0 ) ++ { ++ if( pos <= argc - 2 ) ++ { ++ user = QString::fromLocal8Bit( argv[ pos + 1] ); ++ numOptions +=2; ++ pos++; ++ } ++ else ++ { ++ cerr << "Missing username for '--user' option!" << endl << endl; ++ showHelp( -1 ); ++ } ++ } ++ else if( strcmp( argv[ pos ], "--all-users" ) == 0 ) ++ { ++ user = "*"; ++ numOptions ++; ++ } ++ else if( strcmp( argv[ pos ], "--list-sessions" ) == 0 ) ++ { ++ session = QuerySessions; ++ numOptions ++; ++ } ++ else if( strcmp( argv[ pos ], "--all-sessions" ) == 0 ) ++ { ++ session = AllSessions; ++ numOptions ++; ++ } ++ else if( argv[ pos ][ 0 ] == '-' ) ++ { ++ cerr << "Unknown command-line option '" << argv[ pos ] ++ << "'." << endl << endl; ++ showHelp( -1 ); ++ } ++ else ++ break; // End of options ++ } ++ ++ argc -= numOptions; ++ ++ QCStringList args; ++ for( int i = numOptions; i < argc + numOptions - 1; i++ ) ++ args.append( argv[ i + 1 ] ); ++ ++ if( readStdin && args.count() < 3 ) ++ { ++ cerr << "--pipe option only supported for function calls!" << endl << endl; ++ showHelp( -1 ); ++ } ++ ++ if( user == "*" && args.count() < 3 && session != QuerySessions ) ++ { ++ cerr << "ERROR: The --all-users option is only supported for function calls!" << endl << endl; ++ showHelp( -1 ); ++ } ++ ++ if( session == QuerySessions && !args.isEmpty() ) ++ { ++ cerr << "ERROR: The --list-sessions option cannot be used for actual DCOP calls!" << endl << endl; ++ showHelp( -1 ); ++ } ++ ++ if( session == QuerySessions && user.isEmpty() ) ++ { ++ cerr << "ERROR: The --list-sessions option can only be used with the --user or" << endl ++ << "--all-users options!" << endl << endl; ++ showHelp( -1 ); ++ } ++ ++ if( session != DefaultSession && session != QuerySessions && ++ args.count() < 3 ) ++ { ++ cerr << "ERROR: The --session and --all-sessions options are only supported for function" << endl ++ << "calls!" << endl << endl; ++ showHelp( -1 ); ++ } ++ ++ UserList users; ++ if( user == "*" ) ++ users = userList(); ++ else if( !user.isEmpty() ) ++ users[ user ] = userList()[ user ]; ++ ++ runDCOP( args, users, session, sessionName, readStdin ); + + return 0; + } ++ ++// vim: set ts=8 sts=4 sw=4 noet: ++ +diff -aur dcop/client/dcopfind.cpp dcop2/client/dcopfind.cpp +--- dcop/client/dcopfind.cpp Wed Jan 30 22:38:07 2002 ++++ dcop2/client/dcopfind.cpp Wed Jan 30 22:37:04 2002 +@@ -36,7 +36,7 @@ + static bool bAppIdOnly = 0; + static bool bLaunchApp = 0; + +-bool findObject( const char* app, const char* obj, const char* func, int argc, char** args ) ++bool findObject( const char* app, const char* obj, const char* func, QCStringList args ) + { + QString f = func; // Qt is better with unicode strings, so use one. + int left = f.find( '(' ); +@@ -118,7 +118,7 @@ + f = fc; + } + +- if ( (int) types.count() != argc ) { ++ if ( types.count() != args.count() ) { + qWarning( "arguments do not match" ); + exit(1); + } +@@ -128,9 +128,9 @@ + + int i = 0; + for ( QStringList::Iterator it = types.begin(); it != types.end(); ++it ) { +- marshall(arg, argc, args, i, *it); ++ marshall(arg, args, i, *it); + } +- if ( (int) i != argc ) { ++ if ( (uint) i != args.count() ) { + qWarning( "arguments do not match" ); + exit(1); + } +@@ -221,7 +221,11 @@ + argc = 0; + } + +- findObject( app, objid, function, argc, args ); ++ QCStringList params; ++ for( int i = 0; i < argc; i++ ) ++ params.append( args[ i ] ); ++ ++ findObject( app, objid, function, params ); + + return 0; + } +diff -aur dcop/client/marshall.cpp dcop2/client/marshall.cpp +--- dcop/client/marshall.cpp Wed Jan 30 22:38:07 2002 ++++ dcop2/client/marshall.cpp Wed Jan 30 22:37:04 2002 +@@ -242,108 +242,110 @@ + + } + +-void marshall(QDataStream &arg, int argc, char **argv, int &i, QString type) ++void marshall( QDataStream &arg, QCStringList args, uint &i, QString type ) + { +- if (type == "QStringList") +- type = "QValueList"; +- if (type == "QCStringList") +- type = "QValueList"; +- if (i >= argc) +- { +- qWarning("Not enough arguments."); +- exit(1); +- } +- QString s = QString::fromLocal8Bit(argv[i]); +- +- if ( type == "int" ) +- arg << s.toInt(); +- else if ( type == "uint" ) +- arg << s.toUInt(); +- else if ( type == "unsigned" ) +- arg << s.toUInt(); +- else if ( type == "unsigned int" ) +- arg << s.toUInt(); +- else if ( type == "long" ) +- arg << s.toLong(); +- else if ( type == "long int" ) +- arg << s.toLong(); +- else if ( type == "unsigned long" ) +- arg << s.toULong(); +- else if ( type == "unsigned long int" ) +- arg << s.toULong(); +- else if ( type == "float" ) +- arg << s.toFloat(); +- else if ( type == "double" ) +- arg << s.toDouble(); +- else if ( type == "bool" ) +- arg << mkBool( s ); +- else if ( type == "QString" ) +- arg << s; +- else if ( type == "QCString" ) +- arg << QCString( argv[i] ); +- else if ( type == "QColor" ) +- arg << mkColor( s ); +- else if ( type == "QPoint" ) +- arg << mkPoint( s ); +- else if ( type == "QSize" ) +- arg << mkSize( s ); +- else if ( type == "QRect" ) +- arg << mkRect( s ); +- else if ( type == "QVariant" ) { +- if ( s == "true" || s == "false" ) +- arg << QVariant( mkBool( s ), 42 ); +- else if ( s.left( 4 ) == "int(" ) +- arg << QVariant( s.mid(4, s.length()-5).toInt() ); +- else if ( s.left( 7 ) == "QPoint(" ) +- arg << QVariant( mkPoint( s.mid(7, s.length()-8) ) ); +- else if ( s.left( 6 ) == "QSize(" ) +- arg << QVariant( mkSize( s.mid(6, s.length()-7) ) ); +- else if ( s.left( 6 ) == "QRect(" ) +- arg << QVariant( mkRect( s.mid(6, s.length()-7) ) ); +- else if ( s.left( 7 ) == "QColor(" ) +- arg << QVariant( mkColor( s.mid(7, s.length()-8) ) ); +- else +- arg << QVariant( s ); +- } else if ( type.startsWith("QValueList<")) { +- type = type.mid(11, type.length() - 12); +- QStringList list; +- QString delim = s; +- if (delim == "[") +- delim = "]"; +- if (delim == "(") +- delim = ")"; +- i++; +- QByteArray dummy_data; +- QDataStream dummy_arg(dummy_data, IO_WriteOnly); ++ if (type == "QStringList") ++ type = "QValueList"; ++ if (type == "QCStringList") ++ type = "QValueList"; ++ if( i > args.count() ) ++ { ++ qWarning("Not enough arguments."); ++ exit(1); ++ } ++ QString s = QString::fromLocal8Bit( args[ i ] ); + +- int j = i; +- int count = 0; +- // Parse list to get the count +- while (true) { +- if (j >= argc) +- { +- qWarning("List end-delimiter '%s' not found.", delim.latin1()); +- exit(1); +- } +- if (argv[j] == delim) break; +- marshall(dummy_arg, argc, argv, j, type); +- count++; +- } +- arg << (Q_UINT32) count; +- // Parse the list for real +- while (true) { +- if (i >= argc) +- { +- qWarning("List end-delimiter '%s' not found.", delim.latin1()); +- exit(1); +- } +- if (argv[i] == delim) break; +- marshall(arg, argc, argv, i, type); +- } +- } else { +- qWarning( "cannot handle datatype '%s'", type.latin1() ); +- exit(1); +- } ++ if ( type == "int" ) ++ arg << s.toInt(); ++ else if ( type == "uint" ) ++ arg << s.toUInt(); ++ else if ( type == "unsigned" ) ++ arg << s.toUInt(); ++ else if ( type == "unsigned int" ) ++ arg << s.toUInt(); ++ else if ( type == "long" ) ++ arg << s.toLong(); ++ else if ( type == "long int" ) ++ arg << s.toLong(); ++ else if ( type == "unsigned long" ) ++ arg << s.toULong(); ++ else if ( type == "unsigned long int" ) ++ arg << s.toULong(); ++ else if ( type == "float" ) ++ arg << s.toFloat(); ++ else if ( type == "double" ) ++ arg << s.toDouble(); ++ else if ( type == "bool" ) ++ arg << mkBool( s ); ++ else if ( type == "QString" ) ++ arg << s; ++ else if ( type == "QCString" ) ++ arg << QCString( args[ i ] ); ++ else if ( type == "QColor" ) ++ arg << mkColor( s ); ++ else if ( type == "QPoint" ) ++ arg << mkPoint( s ); ++ else if ( type == "QSize" ) ++ arg << mkSize( s ); ++ else if ( type == "QRect" ) ++ arg << mkRect( s ); ++ else if ( type == "QVariant" ) { ++ if ( s == "true" || s == "false" ) ++ arg << QVariant( mkBool( s ), 42 ); ++ else if ( s.left( 4 ) == "int(" ) ++ arg << QVariant( s.mid(4, s.length()-5).toInt() ); ++ else if ( s.left( 7 ) == "QPoint(" ) ++ arg << QVariant( mkPoint( s.mid(7, s.length()-8) ) ); ++ else if ( s.left( 6 ) == "QSize(" ) ++ arg << QVariant( mkSize( s.mid(6, s.length()-7) ) ); ++ else if ( s.left( 6 ) == "QRect(" ) ++ arg << QVariant( mkRect( s.mid(6, s.length()-7) ) ); ++ else if ( s.left( 7 ) == "QColor(" ) ++ arg << QVariant( mkColor( s.mid(7, s.length()-8) ) ); ++ else ++ arg << QVariant( s ); ++ } else if ( type.startsWith("QValueList<")) { ++ type = type.mid(11, type.length() - 12); ++ QStringList list; ++ QString delim = s; ++ if (delim == "[") ++ delim = "]"; ++ if (delim == "(") ++ delim = ")"; + i++; ++ QByteArray dummy_data; ++ QDataStream dummy_arg(dummy_data, IO_WriteOnly); ++ ++ uint j = i; ++ uint count = 0; ++ // Parse list to get the count ++ while (true) { ++ if( j > args.count() ) ++ { ++ qWarning("List end-delimiter '%s' not found.", delim.latin1()); ++ exit(1); ++ } ++ if( QString::fromLocal8Bit( args[ j ] ) == delim ) ++ break; ++ marshall( dummy_arg, args, j, type ); ++ count++; ++ } ++ arg << (Q_UINT32) count; ++ // Parse the list for real ++ while (true) { ++ if( i > args.count() ) ++ { ++ qWarning("List end-delimiter '%s' not found.", delim.latin1()); ++ exit(1); ++ } ++ if( QString::fromLocal8Bit( args[ i ] ) == delim ) ++ break; ++ marshall( arg, args, i, type ); ++ } ++ } else { ++ qWarning( "cannot handle datatype '%s'", type.latin1() ); ++ exit(1); ++ } ++ i++; + } + diff --git a/kompare/tests/diff/unifiedp.diff b/kompare/tests/diff/unifiedp.diff new file mode 100644 index 00000000..891b8b8d --- /dev/null +++ b/kompare/tests/diff/unifiedp.diff @@ -0,0 +1,19 @@ +--- /home/John/lao Thu Apr 12 11:09:30 2001 ++++ /home/John/tzu Sat Jul 28 13:23:25 2001 +@@ -1,7 +1,6 @@ +-The Way that can be told of is not the eternal Way; +-The name that can be named is not the eternal name. + The Nameless is the origin of Heaven and Earth; +-The Named is the mother of all things. ++The named is the mother of all things. ++ + Therefore let there always be non-being, + so we may see their subtlety, + And let there always be being, +@@ -9,3 +8,6 @@ And let there always be being, + The two are the same, + But after they are produced, + they have different names. ++They both may be called deep and profound. ++Deeper and more profound, ++The door of all subtleties! diff --git a/kompare/tests/perforce/context.diff b/kompare/tests/perforce/context.diff new file mode 100644 index 00000000..a25c2461 --- /dev/null +++ b/kompare/tests/perforce/context.diff @@ -0,0 +1,8 @@ +==== //depot/testje#2 - /home/bruggie/perforce-repo/testje ==== +*************** +*** 3,5 **** +--- 3,6 ---- + + but i + 'll see what this is all about later on ++ More lines for #3 to see what happens in a multifile diff diff --git a/kompare/tests/perforce/contextm.diff b/kompare/tests/perforce/contextm.diff new file mode 100644 index 00000000..07380b31 --- /dev/null +++ b/kompare/tests/perforce/contextm.diff @@ -0,0 +1,23 @@ +==== //depot/craphola#1 - /home/bruggie/perforce-repo/craphola ==== +*************** +*** 2,4 **** +--- 2,11 ---- + I'm just adding this file to see what it does with multiple file that have differences + + Now i'll add some text more later in the #2 revision but that has to wait a bit ++ ++ This will be a copy and paste of the previous lines just to have some changes for revision 2 of this file ++ ++ This is another t4extfile used to test the perforce diff stuff ++ I'm just adding this file to see what it does with multiple file that have differences ++ ++ Now i'll add some text more later in the #2 revision but that has to wait a bit +==== //depot/testje#2 - /home/bruggie/perforce-repo/testje ==== +*************** +*** 3,5 **** +--- 3,7 ---- + + but i + 'll see what this is all about later on ++ ++ More lines for #3 to see what happens in a multifile diff diff --git a/kompare/tests/perforce/rcs.diff b/kompare/tests/perforce/rcs.diff new file mode 100644 index 00000000..ce99087d --- /dev/null +++ b/kompare/tests/perforce/rcs.diff @@ -0,0 +1,3 @@ +==== //depot/testje#2 - /home/bruggie/perforce-repo/testje ==== +a5 1 +More lines for #3 to see what happens in a multifile diff diff --git a/kompare/tests/perforce/rcsm.diff b/kompare/tests/perforce/rcsm.diff new file mode 100644 index 00000000..bee028a0 --- /dev/null +++ b/kompare/tests/perforce/rcsm.diff @@ -0,0 +1,12 @@ +==== //depot/craphola#1 - /home/bruggie/perforce-repo/craphola ==== +a4 7 + +This will be a copy and paste of the previous lines just to have some changes for revision 2 of this file + +This is another t4extfile used to test the perforce diff stuff +I'm just adding this file to see what it does with multiple file that have differences + +Now i'll add some text more later in the #2 revision but that has to wait a bit +==== //depot/testje#2 - /home/bruggie/perforce-repo/testje ==== +a5 2 +More lines for #3 to see what happens in a multifile diff diff --git a/kompare/tests/perforce/unified.diff b/kompare/tests/perforce/unified.diff new file mode 100644 index 00000000..f9235ba9 --- /dev/null +++ b/kompare/tests/perforce/unified.diff @@ -0,0 +1,6 @@ +==== //depot/testje#2 - /home/bruggie/perforce-repo/testje ==== +@@ -3,3 +3,4 @@ + + but i + 'll see what this is all about later on ++More lines for #3 to see what happens in a multifile diff diff --git a/kompare/tests/perforce/unifiedm.diff b/kompare/tests/perforce/unifiedm.diff new file mode 100644 index 00000000..6aa832e7 --- /dev/null +++ b/kompare/tests/perforce/unifiedm.diff @@ -0,0 +1,19 @@ +==== //depot/craphola#1 - /home/bruggie/perforce-repo/craphola ==== +@@ -2,3 +2,10 @@ + I'm just adding this file to see what it does with multiple file that have differences + + Now i'll add some text more later in the #2 revision but that has to wait a bit ++ ++This will be a copy and paste of the previous lines just to have some changes for revision 2 of this file ++ ++This is another t4extfile used to test the perforce diff stuff ++I'm just adding this file to see what it does with multiple file that have differences ++ ++Now i'll add some text more later in the #2 revision but that has to wait a bit +==== //depot/testje#2 - /home/bruggie/perforce-repo/testje ==== +@@ -3,3 +3,5 @@ + + but i + 'll see what this is all about later on ++ ++More lines for #3 to see what happens in a multifile diff diff --git a/kompare/tests/subversion/context.diff b/kompare/tests/subversion/context.diff new file mode 100644 index 00000000..b8380037 --- /dev/null +++ b/kompare/tests/subversion/context.diff @@ -0,0 +1,9 @@ +Index: NEWS +=================================================================== +*** NEWS +--- NEWS Sun Sep 22 14:34:37 2002 +*************** +*** 1 **** +! +--- 1 ---- +! just a fake modif for kompare tests diff --git a/kompare/tests/subversion/contextm.diff b/kompare/tests/subversion/contextm.diff new file mode 100644 index 00000000..fbb61263 --- /dev/null +++ b/kompare/tests/subversion/contextm.diff @@ -0,0 +1,180 @@ +Index: NEWS +=================================================================== +*** NEWS +--- NEWS Sun Sep 22 14:34:37 2002 +*************** +*** 1 **** +! +--- 1 ---- +! just a fake modif for kompare tests +Index: README +=================================================================== +*** README +--- README Fri Sep 13 23:05:48 2002 +*************** +*** 1,117 **** +! Vim KPart +! +! +! by Philippe Fremy +! +! +! Okay, I made it : a Vim KPart! +! +! This means that you can have Vim embedded inside Konqueror, and everywhere a +! text ReadWrite or ReadOnly KPart is requested. Actually, there is almost no +! place right now where this is the case in KDE. KMail uses its own editor, +! KDEvelop uses its own editor, Kate uses some more powerful Kate component. +! +! But this only the beginning. Enabling a part in those programs shouldn't be +! much hassle and you can probably help me do it. My hope is really to get +! KDevelop use Vim. +! +! +! ======= OBSOLETE =========== +! Requirements: +! ------------- +! To make this KPart work, you need a graphicial Vim version 6 with the client-server stuff feature activated and with the vim60-vimpart-patch.diff applied. The patch is in this dir. I hope to get it into the main Vim tree. KVim has already the patch but is slightly less stable that the original GVim. A big advantage of KVim is that you get the native KDE dialogs when vim asks a question. +! +! +! ======= OBSOLETE =========== +! Installation: +! ------------- +! To make your vimpart work, you'll have to go into the vimpart directory and run "testVim your_patched_vim". If the test does work, a file goodVim will be created. You will be able to install and use the component. Else, the test will report why it fails (features missing in vim). +! +! +! +! ======= OBSOLETE =========== +! Testing: +! -------- +! If you want to see your component without installing it, you can do the +! following: +! +! 1. configure, build. Then go into the Vimpart subdirectory. +! +! 2. Include the current Vimpart directory in your KDEDIRS: +! export KDEDIRS=`pwd`:$KDEDIR +! +! 3. Symlink .libs to lib +! ln -s .libs lib +! +! 4. Create pseudo share/services dir: +! mkdir share; mkdir share/services; +! +! 5. Symlink to Vimpart.desktop: +! ln -s Vimpart.desktop share/services/Vimpart.desktop +! +! 6. Create a pseudo share/config dir +! mkdir share/config; +! +! 7. Symlink to vimwidgetrc +! ln -s vimwidgetrc share/config/vimwidgetrc +! +! 8. Update the desktop mimetype database: +! kbuildsycoca +! +! To test it, run VimPartShell. Or run konqueror from this dir and click on a +! text file. +! +! +! ======= OBSOLETE =========== +! Remarks: +! -------- +! The initial preference of the Vim KPart is 10. Kate uses 8, so if you install the part, it will override Kate for all the mimetypes. You can always change that by manually editing the initial preference in the desktop file or by simply selecting which editor you prefer for which mimetype in the control center. +! +! If you find some mimetype not handled by the Vim KPart although they should be, send me a patch! +! +! +! +! How it works: +! ------------- +! At the beginning, we started to write KVim, a port of GVim to KDE to make +! it possible to embed Vim inside KDE. But with the latest version of Vim, it +! turns out that it is not necessary to have a native Vim. +! +! +! I use QXembed, a widget which can embed any X application if it knows its X Window Id, using some X feature. The patch I provide will make vim displays its window id on stdout when the window is mapped. GVim 6.0 then provides a way to send commands to a Vim window from another process. If you look at the VimWidget source, you will see that 70% of the code is there to handle the communication process. The rest uses the communication channel to send the vim commands needed by kpart and ktexteditor. +! +! As far as I can tell, the part is race-condition free. If you issue many sendNormalCmd and many evalExpr, they are guaranted to be executed sequentially. This has caused me enough problems when it wasn't the case! +! +! +! Qt, KDE2 and KDE3: +! ------------------ +! The VimWidget itself depends very litle on KDE. It is quite easy to port remove the KDE specific stuff, to use it in a Qt only program. In fact, at the beginning, it was only Qt-based. +! +! +! +! ======= OBSOLETE =========== +! Features & TODO: +! ---------------- +! I think most basic features required by an editor widget or part are supported. There are some possible improvement but I would like more feedback to know what really needs to be done. So don't hesitate to write me about your feelings using this. +! +! My TODO list is: +! - restore the editing mode after sendCmd +! - implement KTextEditor interface +! - add some useful actions to the part (like search, ...) +! +! +! +! Feedback: +! --------- +! For the Vim KPart : pfremy@kde.com +! For KVim: pfremy@kde.org, mikmak@freenux.org, orzel@kde.org +! +! +! +! +! +! +! + + + +--- 1,47 ---- +! Yes, that's really a Vim Komponent :) +! Yes, you can have Vim inside KDE apps, you guessed it :) + ++ So, it's designed for KDE 3.x (if someone wants to port it to KDE 2 that ++ should be easy), it uses GVim or KVim (even Motif Vim works) 6.x. ++ It can be used in different apps : ++ - KDevelop (version 3) ++ - Konqueror (as a file viewer) ++ - KWrite ++ - KMail (coming in KDE 3.2) ++ - Kompare, KBabel ........ ;) ++ ++ CONFIGURATION ++ ============= ++ once you compiled and installed it as any other app, ++ start your KDE Control Center, go to the file manager section ++ and open the Vim Component configuration module. ++ Here, you have to select a Vim executable which may be found on ++ your computer (generally /usr/bin/vim) will do it fine for most ++ linux distributions. All you need is that it's a GUI-enabled Vim 6.0 or ++ better. ++ Push the test button, if that's okay then that's should be enough to start ++ using it :) ++ ++ FUNCTIONMENT ++ ============ ++ Philippe Fremy (pfremy@kde.com) wrote the initial version of this kpart. ++ The concept is to start a normal GUI Vim (like gvim), then "embeds" the Vim ++ window into a KDE Widget. ++ It used to be based on the ClientServer feature of Vim (type :help ++ clientserver in Vim for more info) using external processus to control the ++ embedded Vim. That was running quite fine, but was slow :/ ++ We changed that :) ++ Now we communicate directly from the kpart to the embedded Vim thanks to X11 ++ without using externals processus. That's much faster and reliable ;) ++ KVim has also another remote control system using KDE's DCOP communication ++ backend. ++ Currently I would advice people to use DCOP when running KVim and using X11 ++ communication with GVim (DCOP won't work with GVim anyway). ++ There may be some differences in speed, though I have not noticed it here. ++ The most important difference is that DCOP provides a signal system and that can ++ make a difference to improve the interaction between KVim and the hosting ++ application (KDevelop for example). But it's not yet used. + ++ Hope you'll enjoy Vim inside KDE :) ++ Mickael "Mikmak" Marchand (marchand@kde.org) + diff --git a/kompare/tests/subversion/ed.diff b/kompare/tests/subversion/ed.diff new file mode 100644 index 00000000..512b3880 --- /dev/null +++ b/kompare/tests/subversion/ed.diff @@ -0,0 +1,5 @@ +Index: NEWS +=================================================================== +1c +just a fake modif for kompare tests +. diff --git a/kompare/tests/subversion/edm.diff b/kompare/tests/subversion/edm.diff new file mode 100644 index 00000000..dc51b21f --- /dev/null +++ b/kompare/tests/subversion/edm.diff @@ -0,0 +1,57 @@ +Index: NEWS +=================================================================== +1c +just a fake modif for kompare tests +. +Index: README +=================================================================== +116a +Hope you'll enjoy Vim inside KDE :) +Mickael "Mikmak" Marchand (marchand@kde.org) +. +115a +So, it's designed for KDE 3.x (if someone wants to port it to KDE 2 that +should be easy), it uses GVim or KVim (even Motif Vim works) 6.x. +It can be used in different apps : + - KDevelop (version 3) + - Konqueror (as a file viewer) + - KWrite + - KMail (coming in KDE 3.2) + - Kompare, KBabel ........ ;) + +CONFIGURATION +============= +once you compiled and installed it as any other app, +start your KDE Control Center, go to the file manager section +and open the Vim Component configuration module. +Here, you have to select a Vim executable which may be found on +your computer (generally /usr/bin/vim) will do it fine for most +linux distributions. All you need is that it's a GUI-enabled Vim 6.0 or +better. +Push the test button, if that's okay then that's should be enough to start +using it :) + +FUNCTIONMENT +============ +Philippe Fremy (pfremy@kde.com) wrote the initial version of this kpart. +The concept is to start a normal GUI Vim (like gvim), then "embeds" the Vim +window into a KDE Widget. +It used to be based on the ClientServer feature of Vim (type :help +clientserver in Vim for more info) using external processus to control the +embedded Vim. That was running quite fine, but was slow :/ +We changed that :) +Now we communicate directly from the kpart to the embedded Vim thanks to X11 +without using externals processus. That's much faster and reliable ;) +KVim has also another remote control system using KDE's DCOP communication +backend. +Currently I would advice people to use DCOP when running KVim and using X11 +communication with GVim (DCOP won't work with GVim anyway). +There may be some differences in speed, though I have not noticed it here. +The most important difference is that DCOP provides a signal system and that can +make a difference to improve the interaction between KVim and the hosting +application (KDevelop for example). But it's not yet used. +. +1,114c +Yes, that's really a Vim Komponent :) +Yes, you can have Vim inside KDE apps, you guessed it :) +. diff --git a/kompare/tests/subversion/normal.diff b/kompare/tests/subversion/normal.diff new file mode 100644 index 00000000..853cc219 --- /dev/null +++ b/kompare/tests/subversion/normal.diff @@ -0,0 +1,6 @@ +Index: NEWS +=================================================================== +1c1 +< +--- +> just a fake modif for kompare tests diff --git a/kompare/tests/subversion/normalm.diff b/kompare/tests/subversion/normalm.diff new file mode 100644 index 00000000..f526a3b0 --- /dev/null +++ b/kompare/tests/subversion/normalm.diff @@ -0,0 +1,170 @@ +Index: NEWS +=================================================================== +1c1 +< +--- +> just a fake modif for kompare tests +Index: README +=================================================================== +1,114c1,2 +< Vim KPart +< +< +< by Philippe Fremy +< +< +< Okay, I made it : a Vim KPart! +< +< This means that you can have Vim embedded inside Konqueror, and everywhere a +< text ReadWrite or ReadOnly KPart is requested. Actually, there is almost no +< place right now where this is the case in KDE. KMail uses its own editor, +< KDEvelop uses its own editor, Kate uses some more powerful Kate component. +< +< But this only the beginning. Enabling a part in those programs shouldn't be +< much hassle and you can probably help me do it. My hope is really to get +< KDevelop use Vim. +< +< +< ======= OBSOLETE =========== +< Requirements: +< ------------- +< To make this KPart work, you need a graphicial Vim version 6 with the client-server stuff feature activated and with the vim60-vimpart-patch.diff applied. The patch is in this dir. I hope to get it into the main Vim tree. KVim has already the patch but is slightly less stable that the original GVim. A big advantage of KVim is that you get the native KDE dialogs when vim asks a question. +< +< +< ======= OBSOLETE =========== +< Installation: +< ------------- +< To make your vimpart work, you'll have to go into the vimpart directory and run "testVim your_patched_vim". If the test does work, a file goodVim will be created. You will be able to install and use the component. Else, the test will report why it fails (features missing in vim). +< +< +< +< ======= OBSOLETE =========== +< Testing: +< -------- +< If you want to see your component without installing it, you can do the +< following: +< +< 1. configure, build. Then go into the Vimpart subdirectory. +< +< 2. Include the current Vimpart directory in your KDEDIRS: +< export KDEDIRS=`pwd`:$KDEDIR +< +< 3. Symlink .libs to lib +< ln -s .libs lib +< +< 4. Create pseudo share/services dir: +< mkdir share; mkdir share/services; +< +< 5. Symlink to Vimpart.desktop: +< ln -s Vimpart.desktop share/services/Vimpart.desktop +< +< 6. Create a pseudo share/config dir +< mkdir share/config; +< +< 7. Symlink to vimwidgetrc +< ln -s vimwidgetrc share/config/vimwidgetrc +< +< 8. Update the desktop mimetype database: +< kbuildsycoca +< +< To test it, run VimPartShell. Or run konqueror from this dir and click on a +< text file. +< +< +< ======= OBSOLETE =========== +< Remarks: +< -------- +< The initial preference of the Vim KPart is 10. Kate uses 8, so if you install the part, it will override Kate for all the mimetypes. You can always change that by manually editing the initial preference in the desktop file or by simply selecting which editor you prefer for which mimetype in the control center. +< +< If you find some mimetype not handled by the Vim KPart although they should be, send me a patch! +< +< +< +< How it works: +< ------------- +< At the beginning, we started to write KVim, a port of GVim to KDE to make +< it possible to embed Vim inside KDE. But with the latest version of Vim, it +< turns out that it is not necessary to have a native Vim. +< +< +< I use QXembed, a widget which can embed any X application if it knows its X Window Id, using some X feature. The patch I provide will make vim displays its window id on stdout when the window is mapped. GVim 6.0 then provides a way to send commands to a Vim window from another process. If you look at the VimWidget source, you will see that 70% of the code is there to handle the communication process. The rest uses the communication channel to send the vim commands needed by kpart and ktexteditor. +< +< As far as I can tell, the part is race-condition free. If you issue many sendNormalCmd and many evalExpr, they are guaranted to be executed sequentially. This has caused me enough problems when it wasn't the case! +< +< +< Qt, KDE2 and KDE3: +< ------------------ +< The VimWidget itself depends very litle on KDE. It is quite easy to port remove the KDE specific stuff, to use it in a Qt only program. In fact, at the beginning, it was only Qt-based. +< +< +< +< ======= OBSOLETE =========== +< Features & TODO: +< ---------------- +< I think most basic features required by an editor widget or part are supported. There are some possible improvement but I would like more feedback to know what really needs to be done. So don't hesitate to write me about your feelings using this. +< +< My TODO list is: +< - restore the editing mode after sendCmd +< - implement KTextEditor interface +< - add some useful actions to the part (like search, ...) +< +< +< +< Feedback: +< --------- +< For the Vim KPart : pfremy@kde.com +< For KVim: pfremy@kde.org, mikmak@freenux.org, orzel@kde.org +< +< +< +< +< +< +< +--- +> Yes, that's really a Vim Komponent :) +> Yes, you can have Vim inside KDE apps, you guessed it :) +115a4,43 +> So, it's designed for KDE 3.x (if someone wants to port it to KDE 2 that +> should be easy), it uses GVim or KVim (even Motif Vim works) 6.x. +> It can be used in different apps : +> - KDevelop (version 3) +> - Konqueror (as a file viewer) +> - KWrite +> - KMail (coming in KDE 3.2) +> - Kompare, KBabel ........ ;) +> +> CONFIGURATION +> ============= +> once you compiled and installed it as any other app, +> start your KDE Control Center, go to the file manager section +> and open the Vim Component configuration module. +> Here, you have to select a Vim executable which may be found on +> your computer (generally /usr/bin/vim) will do it fine for most +> linux distributions. All you need is that it's a GUI-enabled Vim 6.0 or +> better. +> Push the test button, if that's okay then that's should be enough to start +> using it :) +> +> FUNCTIONMENT +> ============ +> Philippe Fremy (pfremy@kde.com) wrote the initial version of this kpart. +> The concept is to start a normal GUI Vim (like gvim), then "embeds" the Vim +> window into a KDE Widget. +> It used to be based on the ClientServer feature of Vim (type :help +> clientserver in Vim for more info) using external processus to control the +> embedded Vim. That was running quite fine, but was slow :/ +> We changed that :) +> Now we communicate directly from the kpart to the embedded Vim thanks to X11 +> without using externals processus. That's much faster and reliable ;) +> KVim has also another remote control system using KDE's DCOP communication +> backend. +> Currently I would advice people to use DCOP when running KVim and using X11 +> communication with GVim (DCOP won't work with GVim anyway). +> There may be some differences in speed, though I have not noticed it here. +> The most important difference is that DCOP provides a signal system and that can +> make a difference to improve the interaction between KVim and the hosting +> application (KDevelop for example). But it's not yet used. +116a45,46 +> Hope you'll enjoy Vim inside KDE :) +> Mickael "Mikmak" Marchand (marchand@kde.org) diff --git a/kompare/tests/subversion/rcs.diff b/kompare/tests/subversion/rcs.diff new file mode 100644 index 00000000..1633c3a3 --- /dev/null +++ b/kompare/tests/subversion/rcs.diff @@ -0,0 +1,5 @@ +Index: NEWS +=================================================================== +d1 1 +a1 1 +just a fake modif for kompare tests diff --git a/kompare/tests/subversion/rcsm.diff b/kompare/tests/subversion/rcsm.diff new file mode 100644 index 00000000..a409cd54 --- /dev/null +++ b/kompare/tests/subversion/rcsm.diff @@ -0,0 +1,55 @@ +Index: NEWS +=================================================================== +d1 1 +a1 1 +just a fake modif for kompare tests +Index: README +=================================================================== +d1 114 +a114 2 +Yes, that's really a Vim Komponent :) +Yes, you can have Vim inside KDE apps, you guessed it :) +a115 40 +So, it's designed for KDE 3.x (if someone wants to port it to KDE 2 that +should be easy), it uses GVim or KVim (even Motif Vim works) 6.x. +It can be used in different apps : + - KDevelop (version 3) + - Konqueror (as a file viewer) + - KWrite + - KMail (coming in KDE 3.2) + - Kompare, KBabel ........ ;) + +CONFIGURATION +============= +once you compiled and installed it as any other app, +start your KDE Control Center, go to the file manager section +and open the Vim Component configuration module. +Here, you have to select a Vim executable which may be found on +your computer (generally /usr/bin/vim) will do it fine for most +linux distributions. All you need is that it's a GUI-enabled Vim 6.0 or +better. +Push the test button, if that's okay then that's should be enough to start +using it :) + +FUNCTIONMENT +============ +Philippe Fremy (pfremy@kde.com) wrote the initial version of this kpart. +The concept is to start a normal GUI Vim (like gvim), then "embeds" the Vim +window into a KDE Widget. +It used to be based on the ClientServer feature of Vim (type :help +clientserver in Vim for more info) using external processus to control the +embedded Vim. That was running quite fine, but was slow :/ +We changed that :) +Now we communicate directly from the kpart to the embedded Vim thanks to X11 +without using externals processus. That's much faster and reliable ;) +KVim has also another remote control system using KDE's DCOP communication +backend. +Currently I would advice people to use DCOP when running KVim and using X11 +communication with GVim (DCOP won't work with GVim anyway). +There may be some differences in speed, though I have not noticed it here. +The most important difference is that DCOP provides a signal system and that can +make a difference to improve the interaction between KVim and the hosting +application (KDevelop for example). But it's not yet used. +a116 2 +Hope you'll enjoy Vim inside KDE :) +Mickael "Mikmak" Marchand (marchand@kde.org) diff --git a/kompare/tests/subversion/unified.diff b/kompare/tests/subversion/unified.diff new file mode 100644 index 00000000..fca49ace --- /dev/null +++ b/kompare/tests/subversion/unified.diff @@ -0,0 +1,7 @@ +Index: NEWS +=================================================================== +--- NEWS ++++ NEWS 2002-09-22 14:34:37.000000000 +0200 +@@ -1 +1 @@ +- ++just a fake modif for kompare tests diff --git a/kompare/tests/subversion/unifiedm.diff b/kompare/tests/subversion/unifiedm.diff new file mode 100644 index 00000000..29a07705 --- /dev/null +++ b/kompare/tests/subversion/unifiedm.diff @@ -0,0 +1,173 @@ +Index: NEWS +=================================================================== +--- NEWS ++++ NEWS 2002-09-22 14:34:37.000000000 +0200 +@@ -1 +1 @@ +- ++just a fake modif for kompare tests +Index: README +=================================================================== +--- README ++++ README 2002-09-13 23:05:48.000000000 +0200 +@@ -1,117 +1,47 @@ +- Vim KPart +- +- +- by Philippe Fremy +- +- +-Okay, I made it : a Vim KPart! +- +-This means that you can have Vim embedded inside Konqueror, and everywhere a +-text ReadWrite or ReadOnly KPart is requested. Actually, there is almost no +-place right now where this is the case in KDE. KMail uses its own editor, +-KDEvelop uses its own editor, Kate uses some more powerful Kate component. +- +-But this only the beginning. Enabling a part in those programs shouldn't be +-much hassle and you can probably help me do it. My hope is really to get +-KDevelop use Vim. +- +- +-======= OBSOLETE =========== +-Requirements: +-------------- +-To make this KPart work, you need a graphicial Vim version 6 with the client-server stuff feature activated and with the vim60-vimpart-patch.diff applied. The patch is in this dir. I hope to get it into the main Vim tree. KVim has already the patch but is slightly less stable that the original GVim. A big advantage of KVim is that you get the native KDE dialogs when vim asks a question. +- +- +-======= OBSOLETE =========== +-Installation: +-------------- +-To make your vimpart work, you'll have to go into the vimpart directory and run "testVim your_patched_vim". If the test does work, a file goodVim will be created. You will be able to install and use the component. Else, the test will report why it fails (features missing in vim). +- +- +- +-======= OBSOLETE =========== +-Testing: +--------- +-If you want to see your component without installing it, you can do the +-following: +- +-1. configure, build. Then go into the Vimpart subdirectory. +- +-2. Include the current Vimpart directory in your KDEDIRS: +-export KDEDIRS=`pwd`:$KDEDIR +- +-3. Symlink .libs to lib +-ln -s .libs lib +- +-4. Create pseudo share/services dir: +-mkdir share; mkdir share/services; +- +-5. Symlink to Vimpart.desktop: +-ln -s Vimpart.desktop share/services/Vimpart.desktop +- +-6. Create a pseudo share/config dir +-mkdir share/config; +- +-7. Symlink to vimwidgetrc +-ln -s vimwidgetrc share/config/vimwidgetrc +- +-8. Update the desktop mimetype database: +-kbuildsycoca +- +-To test it, run VimPartShell. Or run konqueror from this dir and click on a +-text file. +- +- +-======= OBSOLETE =========== +-Remarks: +--------- +-The initial preference of the Vim KPart is 10. Kate uses 8, so if you install the part, it will override Kate for all the mimetypes. You can always change that by manually editing the initial preference in the desktop file or by simply selecting which editor you prefer for which mimetype in the control center. +- +-If you find some mimetype not handled by the Vim KPart although they should be, send me a patch! +- +- +- +-How it works: +-------------- +-At the beginning, we started to write KVim, a port of GVim to KDE to make +-it possible to embed Vim inside KDE. But with the latest version of Vim, it +-turns out that it is not necessary to have a native Vim. +- +- +-I use QXembed, a widget which can embed any X application if it knows its X Window Id, using some X feature. The patch I provide will make vim displays its window id on stdout when the window is mapped. GVim 6.0 then provides a way to send commands to a Vim window from another process. If you look at the VimWidget source, you will see that 70% of the code is there to handle the communication process. The rest uses the communication channel to send the vim commands needed by kpart and ktexteditor. +- +-As far as I can tell, the part is race-condition free. If you issue many sendNormalCmd and many evalExpr, they are guaranted to be executed sequentially. This has caused me enough problems when it wasn't the case! +- +- +-Qt, KDE2 and KDE3: +------------------- +-The VimWidget itself depends very litle on KDE. It is quite easy to port remove the KDE specific stuff, to use it in a Qt only program. In fact, at the beginning, it was only Qt-based. +- +- +- +-======= OBSOLETE =========== +-Features & TODO: +----------------- +-I think most basic features required by an editor widget or part are supported. There are some possible improvement but I would like more feedback to know what really needs to be done. So don't hesitate to write me about your feelings using this. +- +-My TODO list is: +-- restore the editing mode after sendCmd +-- implement KTextEditor interface +-- add some useful actions to the part (like search, ...) +- +- +- +-Feedback: +---------- +-For the Vim KPart : pfremy@kde.com +-For KVim: pfremy@kde.org, mikmak@freenux.org, orzel@kde.org +- +- +- +- +- +- +- ++Yes, that's really a Vim Komponent :) ++Yes, you can have Vim inside KDE apps, you guessed it :) + ++So, it's designed for KDE 3.x (if someone wants to port it to KDE 2 that ++should be easy), it uses GVim or KVim (even Motif Vim works) 6.x. ++It can be used in different apps : ++ - KDevelop (version 3) ++ - Konqueror (as a file viewer) ++ - KWrite ++ - KMail (coming in KDE 3.2) ++ - Kompare, KBabel ........ ;) ++ ++CONFIGURATION ++============= ++once you compiled and installed it as any other app, ++start your KDE Control Center, go to the file manager section ++and open the Vim Component configuration module. ++Here, you have to select a Vim executable which may be found on ++your computer (generally /usr/bin/vim) will do it fine for most ++linux distributions. All you need is that it's a GUI-enabled Vim 6.0 or ++better. ++Push the test button, if that's okay then that's should be enough to start ++using it :) ++ ++FUNCTIONMENT ++============ ++Philippe Fremy (pfremy@kde.com) wrote the initial version of this kpart. ++The concept is to start a normal GUI Vim (like gvim), then "embeds" the Vim ++window into a KDE Widget. ++It used to be based on the ClientServer feature of Vim (type :help ++clientserver in Vim for more info) using external processus to control the ++embedded Vim. That was running quite fine, but was slow :/ ++We changed that :) ++Now we communicate directly from the kpart to the embedded Vim thanks to X11 ++without using externals processus. That's much faster and reliable ;) ++KVim has also another remote control system using KDE's DCOP communication ++backend. ++Currently I would advice people to use DCOP when running KVim and using X11 ++communication with GVim (DCOP won't work with GVim anyway). ++There may be some differences in speed, though I have not noticed it here. ++The most important difference is that DCOP provides a signal system and that can ++make a difference to improve the interaction between KVim and the hosting ++application (KDevelop for example). But it's not yet used. + ++Hope you'll enjoy Vim inside KDE :) ++Mickael "Mikmak" Marchand (marchand@kde.org) + diff --git a/libkomparediff2/CMakeLists.txt b/libkomparediff2/CMakeLists.txt new file mode 100644 index 00000000..13f8b19d --- /dev/null +++ b/libkomparediff2/CMakeLists.txt @@ -0,0 +1,67 @@ +cmake_minimum_required(VERSION 2.6) +project(LibKompareDiff2) + +# search packages used by KDE +find_package(KDE4 REQUIRED) +include(KDE4Defaults) +add_definitions (${QT_DEFINITIONS} ${KDE4_DEFINITIONS}) +include_directories (${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} ${KDE4_INCLUDES}) + +set(komparediff2_SRCS + kompareprocess.cpp + komparemodellist.cpp + diffmodellist.cpp + diffmodel.cpp + difference.cpp + diffhunk.cpp + diffsettings.cpp + settingsbase.cpp + parser.cpp + parserbase.cpp + cvsdiffparser.cpp + diffparser.cpp + perforceparser.cpp + stringlistpair.cpp) + + +kde4_add_library(komparediff2 SHARED ${komparediff2_SRCS}) + +target_link_libraries(komparediff2 ${KDE4_KDECORE_LIBS} ${KDE4_KDEUI_LIBS} ${KDE4_KIO_LIBS}) + +set_target_properties(komparediff2 PROPERTIES VERSION ${GENERIC_LIB_VERSION} +SOVERSION ${GENERIC_LIB_SOVERSION} ) + +install(TARGETS komparediff2 EXPORT LibKompareDiff2Targets ${INSTALL_TARGETS_DEFAULT_ARGS}) + +add_subdirectory(tests) + +install(FILES + diff2export.h + settingsbase.h + diffsettings.h + komparemodellist.h + difference.h + diffmodel.h + diffmodellist.h + marker.h + kompare.h + diffhunk.h + DESTINATION ${INCLUDE_INSTALL_DIR}/libkomparediff2 COMPONENT Devel +) + +# Config.cmake file. +configure_file( "${CMAKE_SOURCE_DIR}/LibKompareDiff2Config.cmake.in" "${CMAKE_BINARY_DIR}/LibKompareDiff2Config.cmake" @ONLY ) + +if(KDE4_USE_COMMON_CMAKE_PACKAGE_CONFIG_DIR) + set(_LibKompareDiff2_CONFIG_DEST "${LIB_INSTALL_DIR}/cmake/libkomparediff2") +else(KDE4_USE_COMMON_CMAKE_PACKAGE_CONFIG_DIR) + set(_LibKompareDiff2_CONFIG_DEST "${LIB_INSTALL_DIR}/libkomparediff2") +endif(KDE4_USE_COMMON_CMAKE_PACKAGE_CONFIG_DIR) + +install( FILES + "${CMAKE_BINARY_DIR}/LibKompareDiff2Config.cmake" + DESTINATION "${_LibKompareDiff2_CONFIG_DEST}" ) +install( EXPORT LibKompareDiff2Targets + DESTINATION "${_LibKompareDiff2_CONFIG_DEST}" + NAMESPACE LibKompareDiff2Import__ + FILE LibKompareDiff2Targets.cmake ) diff --git a/libkomparediff2/COPYING b/libkomparediff2/COPYING new file mode 100644 index 00000000..b95016b2 --- /dev/null +++ b/libkomparediff2/COPYING @@ -0,0 +1,31 @@ +This library is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2 of the License, or +(at your option) any later version. + +See COPYING.GPL2 and/or COPYING.GPL3 for details. + + +Some parts of this library can also be redistributed and/or modified +under the terms of the GNU Library or Lesser General Public License +as published by the Free Software Foundation; either version 2 of +the License, or (at your option) any later version. + +See COPYING.LGPL2, COPYING.LGPL2.1 and/or COPYING.LGPL3 for details. + +If you wish to apply the terms of the GNU General Public License +instead, see Section 3 of the GNU Library General Public License +version 2, Section 3 of the GNU Lesser General Public License +version 2.1 and/or Section 2b of the GNU Lesser General Public +License version 3. + + +The CMake scripts included with this library are also free software; +you can redistribute them and/or modify them under the terms of the +BSD License reproduced in COPYING-CMAKE-SCRIPTS. + + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. diff --git a/libkomparediff2/COPYING-CMAKE-SCRIPTS b/libkomparediff2/COPYING-CMAKE-SCRIPTS new file mode 100644 index 00000000..4b417765 --- /dev/null +++ b/libkomparediff2/COPYING-CMAKE-SCRIPTS @@ -0,0 +1,22 @@ +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 copyright + notice, this list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. +3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +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. diff --git a/libkomparediff2/COPYING.GPL2 b/libkomparediff2/COPYING.GPL2 new file mode 100644 index 00000000..d159169d --- /dev/null +++ b/libkomparediff2/COPYING.GPL2 @@ -0,0 +1,339 @@ + 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 Lesser 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) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public 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) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 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 Lesser General +Public License instead of this License. diff --git a/libkomparediff2/COPYING.GPL3 b/libkomparediff2/COPYING.GPL3 new file mode 100644 index 00000000..94a9ed02 --- /dev/null +++ b/libkomparediff2/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/libkomparediff2/COPYING.LGPL2 b/libkomparediff2/COPYING.LGPL2 new file mode 100644 index 00000000..5bc8fb2c --- /dev/null +++ b/libkomparediff2/COPYING.LGPL2 @@ -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/libkomparediff2/COPYING.LGPL2.1 b/libkomparediff2/COPYING.LGPL2.1 new file mode 100644 index 00000000..4362b491 --- /dev/null +++ b/libkomparediff2/COPYING.LGPL2.1 @@ -0,0 +1,502 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 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 Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + 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 Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +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 and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +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 other code 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. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + 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, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser 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 combine 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) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) 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. + + d) 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. + + e) 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 materials to be 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 with +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 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 +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.1 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/libkomparediff2/COPYING.LGPL3 b/libkomparediff2/COPYING.LGPL3 new file mode 100644 index 00000000..65c5ca88 --- /dev/null +++ b/libkomparediff2/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/libkomparediff2/LibKompareDiff2Config.cmake.in b/libkomparediff2/LibKompareDiff2Config.cmake.in new file mode 100644 index 00000000..caa3b6a0 --- /dev/null +++ b/libkomparediff2/LibKompareDiff2Config.cmake.in @@ -0,0 +1,32 @@ +######################################################################### +# +# LibKompareDiff2 Configuration File +# +# This file sets various CMake Variables +# +# LIBKOMPAREDIFF2_INCLUDE_DIR - The Include Directory for the LIBKOMPAREDIFF2 library +# LIBKOMPAREDIFF2_LIBRARIES - the libkomparediff2 library +# +# Copyright 2008 Andreas Pakulat +# Copyright 2010 Niko Sams +# Copyright 2013 Jeremy Whiting +# Redistribution and use is allowed according to the terms of the BSD license. +###################################################################### + +get_filename_component(_LIBKOMPAREDIFF2_CURRENT_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH) + +if(NOT WIN32) +# This is needed on non-win32 platforms, as lib-install-dir might be in a +# totally different prefix than include-install-dir. So instead hardcode the +# absolute path during buildtime +set( LIBKOMPAREDIFF2_INCLUDE_DIR "@INCLUDE_INSTALL_DIR@/libkomparediff2" ) + +else(NOT WIN32) + +set( LIBKOMPAREDIFF2_INCLUDE_DIR "${_LIBKOMPAREDIFF2_CURRENT_DIR}/../../../include/libkomparediff2" ) + +endif(NOT WIN32) + +include("${CMAKE_CURRENT_LIST_DIR}/LibKompareDiff2Targets.cmake") +set( LIBKOMPAREDIFF2_LIBRARIES LibKompareDiff2Import__komparediff2) + diff --git a/libkomparediff2/Messages.sh b/libkomparediff2/Messages.sh new file mode 100644 index 00000000..69978156 --- /dev/null +++ b/libkomparediff2/Messages.sh @@ -0,0 +1,3 @@ +#! /bin/sh +$EXTRACTRC *.rc */*.rc */*.ui >> rc.cpp || exit 11 +$XGETTEXT `find . -name "*.cpp" -o -name "*.h"` -o $podir/libkomparediff2.pot diff --git a/libkomparediff2/SRCBUILD b/libkomparediff2/SRCBUILD deleted file mode 100644 index e048549c..00000000 --- a/libkomparediff2/SRCBUILD +++ /dev/null @@ -1,21 +0,0 @@ -# Maintainer: Ivailo Monev - -version=4.14.3 -description="Library to compare files and strings" -depends=('kdelibs') -makedepends=('cmake' 'automoc4') -sources=("http://download.kde.org/stable/$version/src/libkomparediff2-$version.tar.xz") - -src_compile() { - mkdir "$SOURCE_DIR/build" && cd "$SOURCE_DIR/build" - cmake ../libkomparediff2-$version \ - -DCMAKE_BUILD_TYPE=Release \ - -DKDE4_BUILD_TESTS=OFF \ - -DCMAKE_INSTALL_PREFIX= - make -} - -src_install() { - cd "$SOURCE_DIR/build" - make DESTDIR="$INSTALL_DIR" install -} diff --git a/libkomparediff2/cvsdiffparser.cpp b/libkomparediff2/cvsdiffparser.cpp new file mode 100644 index 00000000..bc45a121 --- /dev/null +++ b/libkomparediff2/cvsdiffparser.cpp @@ -0,0 +1,156 @@ +/************************************************************************** +** cvsdiffparser.cpp +** ----------------- +** begin : Sun Aug 4 15:05:35 2002 +** Copyright 2002-2004 Otto Bruggeman +***************************************************************************/ +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 "cvsdiffparser.h" + +#include + +#include + +#include "komparemodellist.h" + +using namespace Diff2; + +CVSDiffParser::CVSDiffParser( const KompareModelList* list, const QStringList& diff ) : ParserBase( list, diff ) +{ + // The regexps needed for context cvs diff parsing, the rest is the same as in parserbase.cpp + // third capture in header1 is non optional for cvs diff, it is the revision + m_contextDiffHeader1.setPattern( "\\*\\*\\* ([^\\t]+)\\t([^\\t]+)\\t(.*)\\n" ); + m_contextDiffHeader2.setPattern( "--- ([^\\t]+)\\t([^\\t]+)(|\\t(.*))\\n" ); + + m_normalDiffHeader.setPattern( "Index: (.*)\\n" ); +} + +CVSDiffParser::~CVSDiffParser() +{ +} + +enum Kompare::Format CVSDiffParser::determineFormat() +{ +// kDebug(8101) << "Determining the format of the CVSDiff"; + + QRegExp normalRE ( "[0-9]+[0-9,]*[acd][0-9]+[0-9,]*" ); + QRegExp unifiedRE( "^--- [^\\t]+\\t" ); + QRegExp contextRE( "^\\*\\*\\* [^\\t]+\\t" ); + QRegExp rcsRE ( "^[acd][0-9]+ [0-9]+" ); + QRegExp edRE ( "^[0-9]+[0-9,]*[acd]" ); + + QStringList::ConstIterator it = m_diffLines.begin(); + + while( it != m_diffLines.end() ) + { + if( (*it).indexOf( normalRE, 0 ) == 0 ) + { +// kDebug(8101) << "Difflines are from a Normal diff..."; + return Kompare::Normal; + } + else if( (*it).indexOf( unifiedRE, 0 ) == 0 ) + { +// kDebug(8101) << "Difflines are from a Unified diff..."; + return Kompare::Unified; + } + else if( (*it).indexOf( contextRE, 0 ) == 0 ) + { +// kDebug(8101) << "Difflines are from a Context diff..."; + return Kompare::Context; + } + else if( (*it).indexOf( rcsRE, 0 ) == 0 ) + { +// kDebug(8101) << "Difflines are from a RCS diff..."; + return Kompare::RCS; + } + else if( (*it).indexOf( edRE, 0 ) == 0 ) + { +// kDebug(8101) << "Difflines are from an ED diff..."; + return Kompare::Ed; + } + ++it; + } +// kDebug(8101) << "Difflines are from an unknown diff..."; + return Kompare::UnknownFormat; +} + +bool CVSDiffParser::parseNormalDiffHeader() +{ + kDebug(8101) << "CVSDiffParser::parseNormalDiffHeader()"; + bool result = false; + + QStringList::ConstIterator diffEnd = m_diffLines.end(); + + while ( m_diffIterator != diffEnd ) + { + if ( m_normalDiffHeader.exactMatch( *m_diffIterator ) ) + { + kDebug(8101) << "Matched length Header = " << m_normalDiffHeader.matchedLength(); + kDebug(8101) << "Matched string Header = " << m_normalDiffHeader.cap( 0 ); + + m_currentModel = new DiffModel(); + m_currentModel->setSourceFile ( m_normalDiffHeader.cap( 1 ) ); + m_currentModel->setDestinationFile ( m_normalDiffHeader.cap( 1 ) ); + + result = true; + + ++m_diffIterator; + break; + } + else + { + kDebug(8101) << "No match for: " << ( *m_diffIterator ); + } + ++m_diffIterator; + } + + if ( result == false ) + { + // Set this to the first line again and hope it is a single file diff + m_diffIterator = m_diffLines.begin(); + m_currentModel = new DiffModel(); + m_singleFileDiff = true; + } + + return result; +} + + +bool CVSDiffParser::parseEdDiffHeader() +{ + return false; +} + +bool CVSDiffParser::parseRCSDiffHeader() +{ + return false; +} + +bool CVSDiffParser::parseEdHunkHeader() +{ + return false; +} + +bool CVSDiffParser::parseRCSHunkHeader() +{ + return false; +} + +bool CVSDiffParser::parseEdHunkBody() +{ + return false; +} + +bool CVSDiffParser::parseRCSHunkBody() +{ + return false; +} + diff --git a/libkomparediff2/cvsdiffparser.h b/libkomparediff2/cvsdiffparser.h new file mode 100644 index 00000000..54c7c1e5 --- /dev/null +++ b/libkomparediff2/cvsdiffparser.h @@ -0,0 +1,59 @@ +/************************************************************************** +** cvsdiffparser.h +** ---------------- +** begin : Sun Aug 4 15:05:35 2002 +** Copyright 2002-2004 Otto Bruggeman +***************************************************************************/ +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 CVSDIFF_PARSER_H +#define CVSDIFF_PARSER_H + +#include + +#include "parserbase.h" + +namespace Diff2 +{ + +class KompareModelList; + +class CVSDiffParser : public ParserBase +{ +public: + CVSDiffParser( const KompareModelList* list, const QStringList& diff ); + virtual ~CVSDiffParser(); + +protected: + virtual enum Kompare::Format determineFormat(); + +protected: +// virtual bool parseContextDiffHeader(); + virtual bool parseEdDiffHeader(); + virtual bool parseNormalDiffHeader(); + virtual bool parseRCSDiffHeader(); +// virtual bool parseUnifiedDiffHeader(); + +// virtual bool parseContextHunkHeader(); + virtual bool parseEdHunkHeader(); +// virtual bool parseNormalHunkHeader(); + virtual bool parseRCSHunkHeader(); +// virtual bool parseUnifiedHunkHeader(); + +// virtual bool parseContextHunkBody(); + virtual bool parseEdHunkBody(); +// virtual bool parseNormalHunkBody(); + virtual bool parseRCSHunkBody(); +// virtual bool parseUnifiedHunkBody(); +}; + +} // End of namespace Diff2 + +#endif diff --git a/libkomparediff2/diff2export.h b/libkomparediff2/diff2export.h new file mode 100644 index 00000000..5551a8de --- /dev/null +++ b/libkomparediff2/diff2export.h @@ -0,0 +1,37 @@ +/*************************************************************************** + * Copyright 2007 Andreas Pakulat * + * Copyright 2006 Matt Rogers * + * Copyright 2004 Jarosław Staniek * + * * + * 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 LIBDIFF2EXPORT_H +#define LIBDIFF2EXPORT_H + +/* needed for KDE_EXPORT macros */ +#include + +#ifndef DIFF2_EXPORT +# ifdef MAKE_KOMPAREDIFF2_LIB +# define DIFF2_EXPORT KDE_EXPORT +# else +# define DIFF2_EXPORT KDE_IMPORT +# endif +#endif + +#endif + diff --git a/libkomparediff2/difference.cpp b/libkomparediff2/difference.cpp new file mode 100644 index 00000000..db455e21 --- /dev/null +++ b/libkomparediff2/difference.cpp @@ -0,0 +1,173 @@ +/*************************************************************************** + difference.cpp + -------------- + begin : Sun Mar 4 2001 + Copyright 2001-2004,2009 Otto Bruggeman + Copyright 2001-2003 John Firebaugh +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 "difference.h" +#include "differencestringpair.h" +#include "levenshteintable.h" + +using namespace Diff2; + +Difference::Difference( int sourceLineNo, int destinationLineNo, int type ) : + QObject(), + m_type( type ), + m_sourceLineNo( sourceLineNo ), + m_destinationLineNo( destinationLineNo ), + m_trackingDestinationLineNo( sourceLineNo ), // The whole patch starts as unapplied + m_applied( false ), + m_conflicts( false ), + m_unsaved( false ) +{ +} + +Difference::~Difference() +{ + qDeleteAll( m_sourceLines ); + qDeleteAll( m_destinationLines ); +} + +void Difference::addSourceLine( QString line ) +{ + m_sourceLines.append( new DifferenceString( line ) ); +} + +void Difference::addDestinationLine( QString line ) +{ + m_destinationLines.append( new DifferenceString( line ) ); +} + +int Difference::sourceLineCount() const +{ + return m_sourceLines.count(); +} + +int Difference::destinationLineCount() const +{ + return m_destinationLines.count(); +} + +int Difference::sourceLineEnd() const +{ + return m_sourceLineNo + m_sourceLines.count(); +} + +int Difference::destinationLineEnd() const +{ + return m_destinationLineNo + m_destinationLines.count(); +} + +int Difference::trackingDestinationLineEnd() const +{ + return m_trackingDestinationLineNo + m_destinationLines.count(); +} + +void Difference::apply( bool apply ) +{ + if ( apply != m_applied ) + { + m_applied = apply; + m_unsaved = !m_unsaved; + emit differenceApplied(this); + } +} + +void Difference::applyQuietly(bool apply) +{ + if ( m_applied != apply ) + { + m_unsaved = !m_unsaved; + m_applied = apply; + } +} + +void Difference::determineInlineDifferences() +{ + if ( m_type != Difference::Change ) + return; + + // Do nothing for now when the slc != dlc + // One could try to find the closest matching destination string for any + // of the source strings but this is compute intensive + int slc = sourceLineCount(); + + if ( slc != destinationLineCount() ) + return; + + LevenshteinTable table; + + for ( int i = 0; i < slc; ++i ) + { + DifferenceString* sl = sourceLineAt( i ); + DifferenceString* dl = destinationLineAt( i ); + DifferenceStringPair* pair = new DifferenceStringPair(sl, dl); + + // return value 0 means something went wrong creating the table so dont bother finding markers + if ( table.createTable( pair ) != 0 ) + table.createListsOfMarkers(); + } +} + +QString Difference::recreateDifference() const +{ + QString difference; + + // source + DifferenceStringListConstIterator stringIt = m_sourceLines.begin(); + DifferenceStringListConstIterator sEnd = m_sourceLines.end(); + + for ( ; stringIt != sEnd; ++stringIt ) + { + switch ( m_type ) + { + case Change: + case Delete: + difference += '-'; + break; + default: + // Insert but this is not possible in source + // Unchanged will be handled in destination + // since they are the same +// kDebug( 8101 ) << "Go away, nothing to do for you in source..."; + continue; + } + difference += (*stringIt)->string(); + } + + //destination + stringIt = m_destinationLines.begin(); + sEnd = m_destinationLines.end(); + + for ( ; stringIt != sEnd; ++stringIt ) + { + switch ( m_type ) + { + case Change: + case Insert: + difference += '+'; + break; + case Unchanged: + difference += ' '; + break; + default: // Delete but this is not possible in destination +// kDebug( 8101 ) << "Go away, nothing to do for you in destination..."; + continue; + } + difference += (*stringIt)->string(); + } + + return difference; +} + diff --git a/libkomparediff2/difference.h b/libkomparediff2/difference.h new file mode 100644 index 00000000..03ab1e02 --- /dev/null +++ b/libkomparediff2/difference.h @@ -0,0 +1,213 @@ +/*************************************************************************** + difference.h + ------------ + begin : Sun Mar 4 2001 + Copyright 2001-2004,2009 Otto Bruggeman + Copyright 2001-2003 John Firebaugh +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 DIFFERENCE_H +#define DIFFERENCE_H + +#include + +#include +#include "diff2export.h" +#include "marker.h" + +class QString; + +namespace Diff2 +{ + +class DIFF2_EXPORT DifferenceString +{ +public: + DifferenceString() + { +// kDebug(8101) << "DifferenceString::DifferenceString()" << endl; + } + explicit DifferenceString( const QString& string, const MarkerList& markerList = MarkerList() ) : + m_string( string ), + m_markerList( markerList ) + { +// kDebug(8101) << "DifferenceString::DifferenceString( " << string << ", " << markerList << " )" << endl; + calculateHash(); + } + DifferenceString( const DifferenceString& ds ) : + m_string( ds.m_string ), + m_conflict( ds.m_conflict ), + m_hash( ds.m_hash ), + m_markerList( ds.m_markerList ) + { +// kDebug(8101) << "DifferenceString::DifferenceString( const DifferenceString& " << ds << " )" << endl; + } + ~DifferenceString() + { + qDeleteAll( m_markerList ); + } + +public: + const QString& string() const + { + return m_string; + } + const QString& conflictString() const + { + return m_conflict; + } + const MarkerList& markerList() + { + return m_markerList; + } + void setString( const QString& string ) + { + m_string = string; + calculateHash(); + } + void setConflictString( const QString& conflict ) + { + m_conflict = conflict; + } + void setMarkerList( const MarkerList& markerList ) + { + m_markerList = markerList; + } + void prepend( Marker* marker ) + { + m_markerList.prepend( marker ); + } + bool operator==( const DifferenceString& ks ) + { + if ( m_hash != ks.m_hash ) + return false; + return m_string == ks.m_string; + } + +protected: + void calculateHash() + { + unsigned short const* str = reinterpret_cast( m_string.unicode() ); + const unsigned int len = m_string.length(); + + m_hash = 1315423911; + + for ( unsigned int i = 0; i < len; i++ ) + { + m_hash ^= ( m_hash << 5 ) + str[i] + ( m_hash >> 2 ); + } + } + +private: + QString m_string; + QString m_conflict; + unsigned int m_hash; + MarkerList m_markerList; +}; + +typedef QVector DifferenceStringList; +typedef QVector::iterator DifferenceStringListIterator; +typedef QVector::const_iterator DifferenceStringListConstIterator; + +class DIFF2_EXPORT Difference : public QObject +{ + Q_OBJECT +public: + enum Type { Change, Insert, Delete, Unchanged }; + +public: + Difference( int sourceLineNo, int destinationLineNo, int type = Difference::Unchanged ); + ~Difference(); + +public: + int type() const { return m_type; }; + + int sourceLineNumber() const { return m_sourceLineNo; } + int destinationLineNumber() const { return m_destinationLineNo; } + + int sourceLineCount() const; + int destinationLineCount() const; + + int sourceLineEnd() const; + int destinationLineEnd() const; + + /// Destination line number that tracks applying/unapplying of other differences + /// Essentially a line number in a patch consisting of applied diffs only + int trackingDestinationLineNumber() const { return m_trackingDestinationLineNo; } + int trackingDestinationLineEnd() const; + void setTrackingDestinationLineNumber( int i ) { m_trackingDestinationLineNo = i; } + + DifferenceString* sourceLineAt( int i ) const { return m_sourceLines[ i ]; } + DifferenceString* destinationLineAt( int i ) const { return m_destinationLines[ i ]; } + + const DifferenceStringList sourceLines() const { return m_sourceLines; } + const DifferenceStringList destinationLines() const { return m_destinationLines; } + + bool hasConflict() const + { + return m_conflicts; + } + void setConflict( bool conflicts ) + { + m_conflicts = conflicts; + } + + bool isUnsaved() const + { + return m_unsaved; + } + void setUnsaved( bool unsaved ) + { + m_unsaved = unsaved; + } + + void apply( bool apply ); + /// Apply without emitting any signals + void applyQuietly( bool apply ); + bool applied() const { return m_applied; } + + void setType( int type ) { m_type = type; } + + void addSourceLine( QString line ); + void addDestinationLine( QString line ); + + /** This method will calculate the differences between the individual strings and store them as Markers */ + void determineInlineDifferences(); + + QString recreateDifference() const; + +signals: + void differenceApplied( Difference* ); + +private: + int m_type; + + int m_sourceLineNo; + int m_destinationLineNo; + int m_trackingDestinationLineNo; + + DifferenceStringList m_sourceLines; + DifferenceStringList m_destinationLines; + + bool m_applied; + bool m_conflicts; + bool m_unsaved; +}; + +typedef QList DifferenceList; +typedef QList::iterator DifferenceListIterator; +typedef QList::const_iterator DifferenceListConstIterator; + +} // End of namespace Diff2 + +#endif + diff --git a/libkomparediff2/differencestringpair.h b/libkomparediff2/differencestringpair.h new file mode 100644 index 00000000..58642227 --- /dev/null +++ b/libkomparediff2/differencestringpair.h @@ -0,0 +1,89 @@ +/* + * This file is part of KDevelop + * Copyright 2011 Dmitry Risenberg + * + * 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 General Public + * 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 DIFFERENCESTRINGPAIR_H +#define DIFFERENCESTRINGPAIR_H + +#include + +#include "difference.h" + +namespace Diff2 { + +class Marker; +class DifferenceString; + +class DifferenceStringPair { +public: + DifferenceStringPair(DifferenceString* first, DifferenceString* second) + : m_first(first), m_second(second), + m_strFirst(' ' + first->string()), m_strSecond(' ' + second->string()), + m_lengthFirst(m_strFirst.length()), m_lengthSecond(m_strSecond.length()), + m_arrayFirst(m_strFirst.unicode()), m_arraySecond(m_strSecond.unicode()) + { + // Actual contents must be indented by 1 + } + bool equal(unsigned int firstIndex, unsigned int secondIndex) const + { + return m_arrayFirst[firstIndex] == m_arraySecond[secondIndex]; + } + unsigned int lengthFirst() const + { + return m_lengthFirst; + } + unsigned int lengthSecond() const + { + return m_lengthSecond; + } + MarkerList markerListFirst() const + { + return m_first->markerList(); + } + MarkerList markerListSecond() const + { + return m_second->markerList(); + } + void prependFirst(Marker* marker) + { + m_first->prepend(marker); + } + void prependSecond(Marker* marker) + { + m_second->prepend(marker); + } + bool needFineGrainedOutput(unsigned int difference) const + { + return difference <= qMax(m_lengthFirst, m_lengthSecond) / 2; + } + const static bool allowReplace = true; +private: + DifferenceString* m_first; + DifferenceString* m_second; + QString m_strFirst; + QString m_strSecond; + unsigned int m_lengthFirst; + unsigned int m_lengthSecond; + const QChar* m_arrayFirst; + const QChar* m_arraySecond; +}; + +} + +#endif // DIFFERENCESTRINGPAIR_H \ No newline at end of file diff --git a/libkomparediff2/diffhunk.cpp b/libkomparediff2/diffhunk.cpp new file mode 100644 index 00000000..b76f3d3f --- /dev/null +++ b/libkomparediff2/diffhunk.cpp @@ -0,0 +1,113 @@ +/*************************************************************************** + diffhunk.cpp + ------------ + begin : Sun Mar 4 2001 + Copyright 2001-2004,2009 Otto Bruggeman + Copyright 2001-2003 John Firebaugh +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 "diffhunk.h" + +#include "difference.h" + +using namespace Diff2; + +DiffHunk::DiffHunk( int sourceLine, int destinationLine, QString function, Type type ) : + m_sourceLine( sourceLine ), + m_destinationLine( destinationLine ), + m_function( function ), + m_type( type ) +{ +} + +DiffHunk::~DiffHunk() +{ +} + +void DiffHunk::add( Difference* diff ) +{ + m_differences.append( diff ); +} + +int DiffHunk::sourceLineCount() const +{ + DifferenceListConstIterator diffIt = m_differences.begin(); + DifferenceListConstIterator dEnd = m_differences.end(); + + int lineCount = 0; + + for ( ; diffIt != dEnd; ++diffIt ) + lineCount += (*diffIt)->sourceLineCount(); + + return lineCount; +} + +int DiffHunk::destinationLineCount() const +{ + DifferenceListConstIterator diffIt = m_differences.begin(); + DifferenceListConstIterator dEnd = m_differences.end(); + + int lineCount = 0; + + for ( ; diffIt != dEnd; ++diffIt ) + lineCount += (*diffIt)->destinationLineCount(); + + return lineCount; +} + +QString DiffHunk::recreateHunk() const +{ + QString hunk; + QString differences; + + // recreate body + DifferenceListConstIterator diffIt = m_differences.begin(); + DifferenceListConstIterator dEnd = m_differences.end(); + + int slc = 0; // source line count + int dlc = 0; // destination line count + for ( ; diffIt != dEnd; ++diffIt ) + { + switch ( (*diffIt)->type() ) + { + case Difference::Unchanged: + case Difference::Change: + slc += (*diffIt)->sourceLineCount(); + dlc += (*diffIt)->destinationLineCount(); + break; + case Difference::Insert: + dlc += (*diffIt)->destinationLineCount(); + break; + case Difference::Delete: + slc += (*diffIt)->sourceLineCount(); + break; + } + differences += (*diffIt)->recreateDifference(); + } + + // recreate header + hunk += QString::fromLatin1( "@@ -%1,%3 +%2,%4 @@" ) + .arg( m_sourceLine ) + .arg( m_destinationLine ) + .arg( slc ) + .arg( dlc ); + + if ( !m_function.isEmpty() ) + hunk += ' ' + m_function; + + hunk += QString::fromLatin1( "\n" ); + + hunk += differences; + + kDebug( 8101 ) << hunk << endl; + return hunk; +} diff --git a/libkomparediff2/diffhunk.h b/libkomparediff2/diffhunk.h new file mode 100644 index 00000000..ad847f03 --- /dev/null +++ b/libkomparediff2/diffhunk.h @@ -0,0 +1,68 @@ +/*************************************************************************** + diffhunk.h + ---------- + begin : Sun Mar 4 2001 + Copyright 2001-2004,2009 Otto Bruggeman + Copyright 2001-2003 John Firebaugh +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 DIFFHUNK_H +#define DIFFHUNK_H + +#include "difference.h" + + +namespace Diff2 +{ + +class Difference; + +class DiffHunk +{ +public: + enum Type { Normal, AddedByBlend }; + +public: + DiffHunk( int sourceLine, int destinationLine, QString function = QString(), Type type = Normal ); + ~DiffHunk(); + + const DifferenceList& differences() const { return m_differences; }; + const QString& function() const { return m_function; }; + + int sourceLineNumber() const { return m_sourceLine; }; + int destinationLineNumber() const { return m_destinationLine; }; + + int sourceLineCount() const; + int destinationLineCount() const; + + Type type() const { return m_type; } + void setType( Type type ) { m_type = type; } + + void add( Difference* diff ); + + QString recreateHunk() const; + +private: + int m_sourceLine; + int m_destinationLine; + DifferenceList m_differences; + QString m_function; + Type m_type; +}; + +typedef QList DiffHunkList; +typedef QList::iterator DiffHunkListIterator; +typedef QList::const_iterator DiffHunkListConstIterator; + +} // End of namespace Diff2 + +#endif diff --git a/libkomparediff2/diffmodel.cpp b/libkomparediff2/diffmodel.cpp new file mode 100644 index 00000000..a42e82dc --- /dev/null +++ b/libkomparediff2/diffmodel.cpp @@ -0,0 +1,588 @@ +/*************************************************************************** + diffmodel.cpp + ------------- + begin : Sun Mar 4 2001 + Copyright 2001-2009 Otto Bruggeman + Copyright 2001-2003 John Firebaugh +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 "diffmodel.h" + +#include + +#include +#include + +#include "difference.h" +#include "diffhunk.h" +#include "levenshteintable.h" +#include "stringlistpair.h" + +using namespace Diff2; + +/** */ +DiffModel::DiffModel( const QString& source, const QString& destination ) : + m_source( source ), + m_destination( destination ), + m_sourcePath( "" ), + m_destinationPath( "" ), + m_sourceFile( "" ), + m_destinationFile( "" ), + m_sourceTimestamp( "" ), + m_destinationTimestamp( "" ), + m_sourceRevision( "" ), + m_destinationRevision( "" ), + m_appliedCount( 0 ), + m_diffIndex( 0 ), + m_selectedDifference( 0 ), + m_blended( false ) +{ + splitSourceInPathAndFileName(); + splitDestinationInPathAndFileName(); +} + +DiffModel::DiffModel() : + m_source( "" ), + m_destination( "" ), + m_sourcePath( "" ), + m_destinationPath( "" ), + m_sourceFile( "" ), + m_destinationFile( "" ), + m_sourceTimestamp( "" ), + m_destinationTimestamp( "" ), + m_sourceRevision( "" ), + m_destinationRevision( "" ), + m_appliedCount( 0 ), + m_diffIndex( 0 ), + m_selectedDifference( 0 ), + m_blended( false ) +{ +} + +/** */ +DiffModel::~DiffModel() +{ + m_selectedDifference = 0; + + qDeleteAll( m_hunks ); + qDeleteAll( m_differences ); +} + +void DiffModel::splitSourceInPathAndFileName() +{ + int pos; + + if( ( pos = m_source.lastIndexOf( "/" ) ) >= 0 ) + m_sourcePath = m_source.mid( 0, pos+1 ); + + if( ( pos = m_source.lastIndexOf( "/" ) ) >= 0 ) + m_sourceFile = m_source.mid( pos+1, m_source.length() - pos ); + else + m_sourceFile = m_source; + + kDebug(8101) << m_source << " was split into " << m_sourcePath << " and " << m_sourceFile << endl; +} + +void DiffModel::splitDestinationInPathAndFileName() +{ + int pos; + + if( ( pos = m_destination.lastIndexOf( "/" ) )>= 0 ) + m_destinationPath = m_destination.mid( 0, pos+1 ); + + if( ( pos = m_destination.lastIndexOf( "/" ) ) >= 0 ) + m_destinationFile = m_destination.mid( pos+1, m_destination.length() - pos ); + else + m_destinationFile = m_destination; + + kDebug(8101) << m_destination << " was split into " << m_destinationPath << " and " << m_destinationFile << endl; +} + +DiffModel& DiffModel::operator=( const DiffModel& model ) +{ + if ( &model != this ) // Guard from self-assignment + { + m_source = model.m_source; + m_destination = model.m_destination; + m_sourcePath = model.m_sourcePath; + m_sourceFile = model.m_sourceFile; + m_sourceTimestamp = model.m_sourceTimestamp; + m_sourceRevision = model.m_sourceRevision; + m_destinationPath = model.m_destinationPath; + m_destinationFile = model.m_destinationFile; + m_destinationTimestamp = model.m_destinationTimestamp; + m_destinationRevision = model.m_destinationRevision; + m_appliedCount = model.m_appliedCount; + + m_diffIndex = model.m_diffIndex; + m_selectedDifference = model.m_selectedDifference; + } + + return *this; +} + +bool DiffModel::operator<( const DiffModel& model ) +{ + if ( localeAwareCompareSource( model ) < 0 ) + return true; + return false; +} + +int DiffModel::localeAwareCompareSource( const DiffModel& model ) +{ + kDebug(8101) << "Path: " << model.m_sourcePath << endl; + kDebug(8101) << "File: " << model.m_sourceFile << endl; + + int result = m_sourcePath.localeAwareCompare( model.m_sourcePath ); + + if ( result == 0 ) + return m_sourceFile.localeAwareCompare( model.m_sourceFile ); + + return result; +} + +QString DiffModel::recreateDiff() const +{ + // For now we'll always return a diff in the diff format + QString diff; + + // recreate header + QString tab = QString::fromLatin1( "\t" ); + QString nl = QString::fromLatin1( "\n" ); + diff += QString::fromLatin1( "--- %1\t%2" ).arg( m_source ).arg( m_sourceTimestamp ); + if ( !m_sourceRevision.isEmpty() ) + diff += tab + m_sourceRevision; + diff += nl; + diff += QString::fromLatin1( "+++ %1\t%2" ).arg( m_destination ).arg( m_destinationTimestamp ); + if ( !m_destinationRevision.isEmpty() ) + diff += tab + m_destinationRevision; + diff += nl; + + // recreate body by iterating over the hunks + DiffHunkListConstIterator hunkIt = m_hunks.begin(); + DiffHunkListConstIterator hEnd = m_hunks.end(); + + for ( ; hunkIt != hEnd; ++hunkIt ) + { + if ((*hunkIt)->type() != DiffHunk::AddedByBlend) + diff += (*hunkIt)->recreateHunk(); + } + + return diff; +} + +Difference* DiffModel::firstDifference() +{ + kDebug(8101) << "DiffModel::firstDifference()" << endl; + m_diffIndex = 0; + kDebug(8101) << "m_diffIndex = " << m_diffIndex << endl; + + m_selectedDifference = m_differences[ m_diffIndex ]; + + return m_selectedDifference; +} + +Difference* DiffModel::lastDifference() +{ + kDebug(8101) << "DiffModel::lastDifference()" << endl; + m_diffIndex = m_differences.count() - 1; + kDebug(8101) << "m_diffIndex = " << m_diffIndex << endl; + + m_selectedDifference = m_differences[ m_diffIndex ]; + + return m_selectedDifference; +} + +Difference* DiffModel::prevDifference() +{ + kDebug(8101) << "DiffModel::prevDifference()" << endl; + if ( m_diffIndex > 0 && --m_diffIndex < m_differences.count() ) + { + kDebug(8101) << "m_diffIndex = " << m_diffIndex << endl; + m_selectedDifference = m_differences[ m_diffIndex ]; + } + else + { + m_selectedDifference = 0; + m_diffIndex = 0; + kDebug(8101) << "m_diffIndex = " << m_diffIndex << endl; + } + + return m_selectedDifference; +} + +Difference* DiffModel::nextDifference() +{ + kDebug(8101) << "DiffModel::nextDifference()" << endl; + if ( ++m_diffIndex < m_differences.count() ) + { + kDebug(8101) << "m_diffIndex = " << m_diffIndex << endl; + m_selectedDifference = m_differences[ m_diffIndex ]; + } + else + { + m_selectedDifference = 0; + m_diffIndex = 0; // just for safety... + kDebug(8101) << "m_diffIndex = " << m_diffIndex << endl; + } + + return m_selectedDifference; +} + +const QString DiffModel::sourceFile() const +{ + return m_sourceFile; +} + +const QString DiffModel::destinationFile() const +{ + return m_destinationFile; +} + +const QString DiffModel::sourcePath() const +{ + return m_sourcePath; +} + +const QString DiffModel::destinationPath() const +{ + return m_destinationPath; +} + +void DiffModel::setSourceFile( QString path ) +{ + m_source = path; + splitSourceInPathAndFileName(); +} + +void DiffModel::setDestinationFile( QString path ) +{ + m_destination = path; + splitDestinationInPathAndFileName(); +} + +void DiffModel::setSourceTimestamp( QString timestamp ) +{ + m_sourceTimestamp = timestamp; +} + +void DiffModel::setDestinationTimestamp( QString timestamp ) +{ + m_destinationTimestamp = timestamp; +} + +void DiffModel::setSourceRevision( QString revision ) +{ + m_sourceRevision = revision; +} + +void DiffModel::setDestinationRevision( QString revision ) +{ + m_destinationRevision = revision; +} + +void DiffModel::addHunk( DiffHunk* hunk ) +{ + m_hunks.append( hunk ); +} + +void DiffModel::addDiff( Difference* diff ) +{ + m_differences.append( diff ); + connect(diff, SIGNAL(differenceApplied(Difference*)), SLOT(slotDifferenceApplied(Difference*))); +} + +bool DiffModel::hasUnsavedChanges( void ) const +{ + DifferenceListConstIterator diffIt = m_differences.begin(); + DifferenceListConstIterator endIt = m_differences.end(); + + for ( ; diffIt != endIt; ++diffIt ) + { + if ( (*diffIt)->isUnsaved() ) + return true; + } + + return false; +} + +void DiffModel::applyDifference( bool apply ) +{ + bool appliedState = m_selectedDifference->applied(); + if ( appliedState == apply ) + { + return; + } + if ( apply && !m_selectedDifference->applied() ) + m_appliedCount++; + else if ( !apply && m_selectedDifference->applied() ) + m_appliedCount--; + + m_selectedDifference->apply( apply ); +} + +int GetDifferenceDelta(Difference* diff) +{ + int delta = diff->destinationLineCount() - diff->sourceLineCount(); + if ( !diff->applied() ) + { + delta = -delta; + } + return delta; +} + +void DiffModel::slotDifferenceApplied(Difference* diff) +{ + int delta = GetDifferenceDelta(diff); + foreach( Difference* current, m_differences ) + { + if ( current->destinationLineNumber() > diff->destinationLineNumber() ) + { + current->setTrackingDestinationLineNumber(current->trackingDestinationLineNumber() + delta); + } + } +} + +void DiffModel::applyAllDifferences( bool apply ) +{ + if ( apply ) + { + m_appliedCount = m_differences.count(); + } + else + { + m_appliedCount = 0; + } + + DifferenceListIterator diffIt = m_differences.begin(); + DifferenceListIterator dEnd = m_differences.end(); + + int totalDelta = 0; + for ( ; diffIt != dEnd; ++diffIt ) + { + (*diffIt)->setTrackingDestinationLineNumber((*diffIt)->trackingDestinationLineNumber() + totalDelta); + bool appliedState = (*diffIt)->applied(); + if ( appliedState == apply ) + { + continue; + } + (*diffIt)->applyQuietly( apply ); + int currentDelta = GetDifferenceDelta(*diffIt); + totalDelta += currentDelta; + } +} + +bool DiffModel::setSelectedDifference( Difference* diff ) +{ + kDebug(8101) << "diff = " << diff << endl; + kDebug(8101) << "m_selectedDifference = " << m_selectedDifference << endl; + + if ( diff != m_selectedDifference ) + { + if ( ( m_differences.indexOf( diff ) ) == -1 ) + return false; + // Do not set m_diffIndex if it cant be found + m_diffIndex = m_differences.indexOf( diff ); + kDebug(8101) << "m_diffIndex = " << m_diffIndex << endl; + m_selectedDifference = diff; + } + + return true; +} + +QPair, QList > DiffModel::linesChanged(const QStringList& oldLines, const QStringList& newLines, int editLineNumber) +{ + // These two will be returned as the function result + QList inserted; + QList removed; + if (oldLines.size() == 0 && newLines.size() == 0) { + return qMakePair(QList(), QList()); + } + int editLineEnd = editLineNumber + oldLines.size(); + // Find the range of differences [iterBegin, iterEnd) that should be updated + // TODO: assume that differences are ordered by starting line. Check that this is always the case + DifferenceList applied; + DifferenceListIterator iterBegin; // first diff ending a line before editLineNo or later + for (iterBegin = m_differences.begin(); iterBegin != m_differences.end(); ++iterBegin) { + // If the difference ends a line before the edit starts, they should be merged if this difference is applied. + // Also it should be merged if it starts on editLineNumber, otherwise there will be two markers for the same line + int lineAfterLast = (*iterBegin)->trackingDestinationLineEnd(); + if (lineAfterLast > editLineNumber || (lineAfterLast == editLineNumber && + ((*iterBegin)->applied() || (*iterBegin)->trackingDestinationLineNumber() == editLineNumber))) { + break; + } + } + DifferenceListIterator iterEnd; + for (iterEnd = iterBegin; iterEnd != m_differences.end(); ++iterEnd) { + // If the difference starts a line after the edit ends, it should still be merged if it is applied + int firstLine = (*iterEnd)->trackingDestinationLineNumber(); + if (firstLine > editLineEnd || (!(*iterEnd)->applied() && firstLine == editLineEnd)) { + break; + } + if ((*iterEnd)->applied()) { + applied.append(*iterEnd); + } + } + + // Compute line numbers in source and destination to which the for diff line sequences (will be created later) + int sourceLineNumber; + int destinationLineNumber; + if (iterBegin == m_differences.end()) { // All existing diffs are after the change + destinationLineNumber = editLineNumber; + if (!m_differences.isEmpty()) { + sourceLineNumber = m_differences.last()->sourceLineEnd() - (m_differences.last()->trackingDestinationLineEnd() - editLineNumber); + } else { + sourceLineNumber = destinationLineNumber; + } + } else if (!(*iterBegin)->applied() || (*iterBegin)->trackingDestinationLineNumber() >= editLineNumber) { + destinationLineNumber = editLineNumber; + sourceLineNumber = (*iterBegin)->sourceLineNumber() - ((*iterBegin)->trackingDestinationLineNumber() - editLineNumber); + } else { + sourceLineNumber = (*iterBegin)->sourceLineNumber(); + destinationLineNumber = (*iterBegin)->trackingDestinationLineNumber(); + } + + // Only the applied differences are of interest, unapplied can be safely removed + DifferenceListConstIterator appliedBegin = applied.constBegin(); + DifferenceListConstIterator appliedEnd = applied.constEnd(); + + // Now create a sequence of lines for the destination file and the corresponding lines in source + QStringList sourceLines; + QStringList destinationLines; + DifferenceListIterator insertPosition; // where to insert the created diffs + if (appliedBegin == appliedEnd) { + destinationLines = newLines; + sourceLines = oldLines; + } else { + // Create the destination line sequence + int firstDestinationLineNumber = (*appliedBegin)->trackingDestinationLineNumber(); + for (int lineNumber = firstDestinationLineNumber; lineNumber < editLineNumber; ++lineNumber) { + destinationLines.append((*appliedBegin)->destinationLineAt(lineNumber - firstDestinationLineNumber)->string()); + } + foreach(const QString& line, newLines) { + destinationLines.append(line); + } + DifferenceListConstIterator appliedLast = appliedEnd; + --appliedLast; + int lastDestinationLineNumber = (*appliedLast)->trackingDestinationLineNumber(); + for (int lineNumber = editLineEnd; lineNumber < (*appliedLast)->trackingDestinationLineEnd(); ++lineNumber) { + destinationLines.append((*appliedLast)->destinationLineAt(lineNumber - lastDestinationLineNumber)->string()); + } + + // Create the source line sequence + if ((*appliedBegin)->trackingDestinationLineNumber() >= editLineNumber) { + for (int i = editLineNumber; i < (*appliedBegin)->trackingDestinationLineNumber(); ++i) { + sourceLines.append(oldLines.at(i - editLineNumber)); + } + } + + for (DifferenceListConstIterator iter = appliedBegin; iter != appliedEnd;) { + int startPos = (*iter)->trackingDestinationLineNumber(); + if ((*iter)->applied()) { + for(int i = 0; i < (*iter)->sourceLineCount(); ++i) { + sourceLines.append((*iter)->sourceLineAt(i)->string()); + } + startPos = (*iter)->trackingDestinationLineEnd(); + } else if (startPos < editLineNumber) { + startPos = editLineNumber; + } + ++iter; + int endPos = (iter == appliedEnd) ? editLineEnd : (*iter)->trackingDestinationLineNumber(); + for (int i = startPos; i < endPos; ++i) { + sourceLines.append(oldLines.at(i - editLineNumber)); + } + } + } + + for (DifferenceListIterator iter = iterBegin; iter != iterEnd; ++iter) { + removed << *iter; + } + insertPosition = m_differences.erase(iterBegin, iterEnd); + + // Compute the Levenshtein table for two line sequences and construct the shortest possible edit script + StringListPair* pair = new StringListPair(sourceLines, destinationLines); + LevenshteinTable table; + table.createTable(pair); + table.createListsOfMarkers(); + MarkerList sourceMarkers = pair->markerListFirst(); + MarkerList destinationMarkers = pair->markerListSecond(); + + int currentSourceListLine = 0; + int currentDestinationListLine = 0; + MarkerListConstIterator sourceMarkerIter = sourceMarkers.constBegin(); + MarkerListConstIterator destinationMarkerIter = destinationMarkers.constBegin(); + const int terminatorLineNumber = sourceLines.size() + destinationLines.size() + 1; // A virtual offset for simpler computation - stands for infinity + + // Process marker lists, converting pairs of Start-End markers into differences. + // Marker in source list only stands for deletion, in source and destination lists - for change, in destination list only - for insertion. + while(sourceMarkerIter != sourceMarkers.constEnd() || destinationMarkerIter != destinationMarkers.constEnd()) { + int nextSourceListLine = sourceMarkerIter != sourceMarkers.constEnd() ? (*sourceMarkerIter)->offset() : terminatorLineNumber; + int nextDestinationListLine = destinationMarkerIter != destinationMarkers.constEnd() ? (*destinationMarkerIter)->offset() : terminatorLineNumber; + + // Advance to the nearest marker + int linesToSkip = qMin(nextDestinationListLine - currentDestinationListLine, nextSourceListLine - currentSourceListLine); + currentSourceListLine += linesToSkip; + currentDestinationListLine += linesToSkip; + Difference* diff = new Difference(sourceLineNumber + currentSourceListLine, destinationLineNumber + currentDestinationListLine); + if (nextSourceListLine == currentSourceListLine) { + processStartMarker(diff, sourceLines, sourceMarkerIter, currentSourceListLine, true); + } + if (nextDestinationListLine == currentDestinationListLine) { + processStartMarker(diff, destinationLines, destinationMarkerIter, currentDestinationListLine, false); + } + computeDiffStats(diff); + Q_ASSERT(diff->type() != Difference::Unchanged); + diff->applyQuietly(true); + diff->setTrackingDestinationLineNumber(diff->destinationLineNumber()); + insertPosition = m_differences.insert(insertPosition, diff); + ++insertPosition; + inserted << diff; + } + // Update line numbers for differences that are after the edit + for (; insertPosition != m_differences.end(); ++insertPosition) { + (*insertPosition)->setTrackingDestinationLineNumber((*insertPosition)->trackingDestinationLineNumber() + (newLines.size() - oldLines.size())); + } + return qMakePair(inserted, removed); +} + +// Some common computing after diff contents have been filled. +void DiffModel::computeDiffStats(Difference* diff) +{ + if (diff->sourceLineCount() > 0 && diff->destinationLineCount() > 0) { + diff->setType(Difference::Change); + } else if (diff->sourceLineCount() > 0) { + diff->setType(Difference::Delete); + } else if (diff->destinationLineCount() > 0) { + diff->setType(Difference::Insert); + } + diff->determineInlineDifferences(); +} + +// Helper method to extract duplicate code from DiffModel::linesChanged +void DiffModel::processStartMarker(Difference* diff, const QStringList& lines, MarkerListConstIterator& currentMarker, int& currentListLine, bool isSource) +{ + Q_ASSERT((*currentMarker)->type() == Marker::Start); + ++currentMarker; + Q_ASSERT((*currentMarker)->type() == Marker::End); + int nextDestinationListLine = (*currentMarker)->offset(); + for (; currentListLine < nextDestinationListLine; ++currentListLine) { + if (isSource) { + diff->addSourceLine(lines.at(currentListLine)); + } else { + diff->addDestinationLine(lines.at(currentListLine)); + } + } + ++currentMarker; + currentListLine = nextDestinationListLine; +} + +/* vim: set ts=4 sw=4 noet: */ diff --git a/libkomparediff2/diffmodel.h b/libkomparediff2/diffmodel.h new file mode 100644 index 00000000..45a80ec3 --- /dev/null +++ b/libkomparediff2/diffmodel.h @@ -0,0 +1,155 @@ +/*************************************************************************** + diffmodel.h + ----------- + begin : Sun Mar 4 2001 + Copyright 2001-2004,2009 Otto Bruggeman + Copyright 2001-2003 John Firebaugh +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 DIFFMODEL_H +#define DIFFMODEL_H + +#include +#include + +#include "diffhunk.h" +#include "kompare.h" +#include "diff2export.h" + +namespace Diff2 +{ + +class DiffHunk; +class Difference; + +class DIFF2_EXPORT DiffModel : public QObject +{ + Q_OBJECT +public: + + DiffModel( const QString& srcBaseURL, const QString& destBaseURL ); + DiffModel(); + ~DiffModel(); + +private: + DiffModel( const DiffModel& ) : QObject() {}; + +public: + int parseDiff( enum Kompare::Format format, const QStringList& list ); + + QString recreateDiff() const; + + int hunkCount() const { return m_hunks.count(); } + int differenceCount() const { return m_differences.count(); } + int appliedCount() const { return m_appliedCount; } + + DiffHunk* hunkAt( int i ) { return ( m_hunks.at( i ) ); } + const Difference* differenceAt( int i ) const { return ( m_differences.at( i ) ); } + Difference* differenceAt( int i ) { return ( m_differences.at( i ) ); } + + DiffHunkList* hunks() { return &m_hunks; } + const DiffHunkList* hunks() const { return &m_hunks; } + DifferenceList* differences() { return &m_differences; } + const DifferenceList* differences() const { return &m_differences; } + + int findDifference( Difference* diff ) const { return m_differences.indexOf( diff ); } + + Difference* firstDifference(); + Difference* lastDifference(); + Difference* prevDifference(); + Difference* nextDifference(); + + const QString source() const { return m_source; } + const QString destination() const { return m_destination; } + const QString sourceFile() const; + const QString destinationFile() const; + const QString sourcePath() const; + const QString destinationPath() const; + const QString sourceTimestamp() const { return m_sourceTimestamp; } + const QString destinationTimestamp() const { return m_destinationTimestamp; } + const QString sourceRevision() const { return m_sourceRevision; } + const QString destinationRevision() const { return m_destinationRevision; } + + void setSourceFile( QString path ); + void setDestinationFile( QString path ); + void setSourceTimestamp( QString timestamp ); + void setDestinationTimestamp( QString timestamp ); + void setSourceRevision( QString revision ); + void setDestinationRevision( QString revision ); + + void addHunk( DiffHunk* hunk ); + void addDiff( Difference* diff ); + bool hasUnsavedChanges() const; + + int diffIndex( void ) const { return m_diffIndex; } + void setDiffIndex( int diffIndex ) { m_diffIndex = diffIndex; } + + void applyDifference( bool apply ); + void applyAllDifferences( bool apply ); + + bool setSelectedDifference( Difference* diff ); + + DiffModel& operator=( const DiffModel& model ); + bool operator<( const DiffModel& model ); + + int localeAwareCompareSource( const DiffModel& model ); + + bool isBlended() const { return m_blended; } + void setBlended( bool blended ) { m_blended = blended; } + + /** + * @p oldlines - lines that were removed. + * @p newLines - lines that were inserted. + * @p startPos - number of line at which the change occured + */ + QPair, QList > linesChanged(const QStringList& oldLines, const QStringList& newLines, int editLineNumber); + +private: + void splitSourceInPathAndFileName(); + void splitDestinationInPathAndFileName(); + void computeDiffStats(Difference* diff); + void processStartMarker(Difference* diff, const QStringList& lines, MarkerListConstIterator& currentMarker, int& currentListLine, bool isSource); + +private: + QString m_source; + QString m_destination; + + QString m_sourcePath; + QString m_destinationPath; + + QString m_sourceFile; + QString m_destinationFile; + + QString m_sourceTimestamp; + QString m_destinationTimestamp; + + QString m_sourceRevision; + QString m_destinationRevision; + + DiffHunkList m_hunks; + DifferenceList m_differences; + + int m_appliedCount; + + int m_diffIndex; + Difference* m_selectedDifference; + + bool m_blended; + +private slots: + void slotDifferenceApplied( Difference* diff ); +}; + +} // End of namespace Diff2 + +#endif + diff --git a/libkomparediff2/diffmodellist.cpp b/libkomparediff2/diffmodellist.cpp new file mode 100644 index 00000000..d76226f0 --- /dev/null +++ b/libkomparediff2/diffmodellist.cpp @@ -0,0 +1,32 @@ +/******************************************************************************* +** +** Filename : diffmodellist.cpp +** Created on : 26 march, 2004 +** Copyright 2004 Otto Bruggeman +** +*******************************************************************************/ + +/******************************************************************************* +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 "diffmodellist.h" + +#include + +using namespace Diff2; + +bool diffModelCompare(DiffModel* model1, DiffModel* model2) +{ + return *model1 < *model2; +} + +void DiffModelList::sort() +{ + qSort(begin(), end(), diffModelCompare); +} diff --git a/libkomparediff2/diffmodellist.h b/libkomparediff2/diffmodellist.h new file mode 100644 index 00000000..edf1d22a --- /dev/null +++ b/libkomparediff2/diffmodellist.h @@ -0,0 +1,51 @@ +/******************************************************************************* +** +** Filename : diffmodellist.h +** Created on : 24 januari, 2004 +** Copyright 2004-2005, 2009 Otto Bruggeman +** +*******************************************************************************/ + +/******************************************************************************* +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 DIFFMODELLIST_H +#define DIFFMODELLIST_H + +#include // include for the base class + +#include "diffmodel.h" +#include "diff2export.h" + +namespace Diff2 +{ + +typedef QList::Iterator DiffModelListIterator; +typedef QList::ConstIterator DiffModelListConstIterator; + +class DIFF2_EXPORT DiffModelList : public QList +{ +public: + DiffModelList() {} + DiffModelList( const DiffModelList &list ) : QList( list ) {} + virtual ~DiffModelList() + { + // Memleak as indicated by valgrind + while ( !isEmpty() ) + delete takeFirst(); + } + +public: + virtual void sort(); + +}; // End of class DiffModelList + +} // End of Namespace Diff2 + +#endif // DIFFMODELLIST_H diff --git a/libkomparediff2/diffparser.cpp b/libkomparediff2/diffparser.cpp new file mode 100644 index 00000000..624baf44 --- /dev/null +++ b/libkomparediff2/diffparser.cpp @@ -0,0 +1,79 @@ +/************************************************************************** +** diffparser.cpp +** -------------- +** begin : Sun Aug 4 15:05:35 2002 +** Copyright 2002-2004 Otto Bruggeman +***************************************************************************/ +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 "diffparser.h" + +#include + +#include + +using namespace Diff2; + +DiffParser::DiffParser( const KompareModelList* list, const QStringList& diff ) : ParserBase( list, diff ) +{ + // The regexps needed for context diff parsing, the rest is the same as in parserbase.cpp + m_contextDiffHeader1.setPattern( "\\*\\*\\* ([^\\t]+)(\\t([^\\t]+))?\\n" ); + m_contextDiffHeader2.setPattern( "--- ([^\\t]+)(\\t([^\\t]+))?\\n" ); +} + +DiffParser::~DiffParser() +{ +} + +enum Kompare::Format DiffParser::determineFormat() +{ + kDebug(8101) << "Determining the format of the diff Diff" << m_diffLines << endl; + + QRegExp normalRE ( "[0-9]+[0-9,]*[acd][0-9]+[0-9,]*" ); + QRegExp unifiedRE( "^--- " ); + QRegExp contextRE( "^\\*\\*\\* " ); + QRegExp rcsRE ( "^[acd][0-9]+ [0-9]+" ); + QRegExp edRE ( "^[0-9]+[0-9,]*[acd]" ); + + QStringList::ConstIterator it = m_diffLines.begin(); + + while( it != m_diffLines.end() ) + { + kDebug(8101) << (*it) << endl; + if( it->indexOf( normalRE, 0 ) == 0 ) + { + kDebug(8101) << "Difflines are from a Normal diff..." << endl; + return Kompare::Normal; + } + else if( it->indexOf( unifiedRE, 0 ) == 0 ) + { + kDebug(8101) << "Difflines are from a Unified diff..." << endl; + return Kompare::Unified; + } + else if( it->indexOf( contextRE, 0 ) == 0 ) + { + kDebug(8101) << "Difflines are from a Context diff..." << endl; + return Kompare::Context; + } + else if( it->indexOf( rcsRE, 0 ) == 0 ) + { + kDebug(8101) << "Difflines are from an RCS diff..." << endl; + return Kompare::RCS; + } + else if( it->indexOf( edRE, 0 ) == 0 ) + { + kDebug(8101) << "Difflines are from an ED diff..." << endl; + return Kompare::Ed; + } + ++it; + } + kDebug(8101) << "Difflines are from an unknown diff..." << endl; + return Kompare::UnknownFormat; +} diff --git a/libkomparediff2/diffparser.h b/libkomparediff2/diffparser.h new file mode 100644 index 00000000..2a389b45 --- /dev/null +++ b/libkomparediff2/diffparser.h @@ -0,0 +1,36 @@ +/************************************************************************** +** diffparser.h +** ------------ +** begin : Sun Aug 4 15:05:35 2002 +** Copyright 2002-2004 Otto Bruggeman +***************************************************************************/ +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 DIFFPARSER_H +#define DIFFPARSER_H + +#include "parserbase.h" + +namespace Diff2 +{ + +class DiffParser : public ParserBase +{ +public: + DiffParser( const KompareModelList* list, const QStringList& diff ); + virtual ~DiffParser(); + +protected: + virtual enum Kompare::Format determineFormat(); +}; + +} // End of namespace Diff2 + +#endif diff --git a/libkomparediff2/diffsettings.cpp b/libkomparediff2/diffsettings.cpp new file mode 100644 index 00000000..081798d3 --- /dev/null +++ b/libkomparediff2/diffsettings.cpp @@ -0,0 +1,109 @@ +/*************************************************************************** + diffsettings.cpp + ---------------- + begin : Sun Mar 4 2001 + Copyright 2001-2004 Otto Bruggeman + Copyright 2007 Kevin Kofler +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 "diffsettings.h" + +#include +#include + +DiffSettings::DiffSettings( QWidget* parent ) + : SettingsBase( parent ), + m_linesOfContext( 0 ), + m_format( Kompare::Unified ), + m_largeFiles( false ), + m_ignoreWhiteSpace( false ), + m_ignoreAllWhiteSpace( false ), + m_ignoreEmptyLines( false ), + m_ignoreChangesDueToTabExpansion( false ), + m_createSmallerDiff( false ), + m_ignoreChangesInCase( false ), + m_showCFunctionChange( false ), + m_convertTabsToSpaces( false ), + m_ignoreRegExp( false ), + m_recursive( false ), + m_newFiles( false ), + m_excludeFilePattern( false ), + m_excludeFilesFile( false ) +{ +} + +DiffSettings::~DiffSettings() +{ +} + +void DiffSettings::loadSettings( KConfig* config ) +{ + KConfigGroup group( config, "Diff Options" ); + m_diffProgram = group.readEntry ( "DiffProgram", "" ); + m_linesOfContext = group.readEntry ( "LinesOfContext", 3 ); + m_largeFiles = group.readEntry( "LargeFiles", true ); + m_ignoreWhiteSpace = group.readEntry( "IgnoreWhiteSpace", false ); + m_ignoreAllWhiteSpace = group.readEntry( "IgnoreAllWhiteSpace", false ); + m_ignoreEmptyLines = group.readEntry( "IgnoreEmptyLines", false ); + m_ignoreChangesDueToTabExpansion = group.readEntry( "IgnoreChangesDueToTabExpansion", false ); + m_ignoreChangesInCase = group.readEntry( "IgnoreChangesInCase", false ); + m_ignoreRegExp = group.readEntry( "IgnoreRegExp", false ); + m_ignoreRegExpText = group.readEntry ( "IgnoreRegExpText", "" ); + m_ignoreRegExpTextHistory = group.readEntry( "IgnoreRegExpTextHistory", QStringList() ); + m_createSmallerDiff = group.readEntry( "CreateSmallerDiff", true ); + m_convertTabsToSpaces = group.readEntry( "ConvertTabsToSpaces", false ); + m_showCFunctionChange = group.readEntry( "ShowCFunctionChange", false ); + m_recursive = group.readEntry( "CompareRecursively", true ); + m_newFiles = group.readEntry( "NewFiles", true ); + + m_format = static_cast( group.readEntry( "Format", (int) Kompare::Unified ) ); + + KConfigGroup group2 ( config, "Exclude File Options" ); + m_excludeFilePattern = group2.readEntry( "Pattern", false ); + m_excludeFilePatternList = group2.readEntry( "PatternList", QStringList() ); + m_excludeFilesFile = group2.readEntry( "File", false ); + m_excludeFilesFileURL = group2.readEntry ( "FileURL", "" ); + m_excludeFilesFileHistoryList = group2.readEntry( "FileHistoryList", QStringList() ); +} + +void DiffSettings::saveSettings( KConfig* config ) +{ + KConfigGroup group( config, "Diff Options" ); + group.writeEntry( "DiffProgram", m_diffProgram ); + group.writeEntry( "LinesOfContext", m_linesOfContext ); + group.writeEntry( "Format", QString(m_format) ); + group.writeEntry( "LargeFiles", m_largeFiles ); + group.writeEntry( "IgnoreWhiteSpace", m_ignoreWhiteSpace ); + group.writeEntry( "IgnoreAllWhiteSpace", m_ignoreAllWhiteSpace ); + group.writeEntry( "IgnoreEmptyLines", m_ignoreEmptyLines ); + group.writeEntry( "IgnoreChangesInCase", m_ignoreChangesInCase ); + group.writeEntry( "IgnoreChangesDueToTabExpansion", m_ignoreChangesDueToTabExpansion ); + group.writeEntry( "IgnoreRegExp", m_ignoreRegExp ); + group.writeEntry( "IgnoreRegExpText", m_ignoreRegExpText ); + group.writeEntry( "IgnoreRegExpTextHistory", m_ignoreRegExpTextHistory ); + group.writeEntry( "CreateSmallerDiff", m_createSmallerDiff ); + group.writeEntry( "ConvertTabsToSpaces", m_convertTabsToSpaces ); + group.writeEntry( "ShowCFunctionChange", m_showCFunctionChange ); + group.writeEntry( "CompareRecursively", m_recursive ); + group.writeEntry( "NewFiles", m_newFiles ); + + KConfigGroup group2( config, "Exclude File Options" ); + group2.writeEntry( "Pattern", m_excludeFilePattern ); + group2.writeEntry( "PatternList", m_excludeFilePatternList ); + group2.writeEntry( "File", m_excludeFilesFile ); + group2.writeEntry( "FileURL", m_excludeFilesFileURL ); + group2.writeEntry( "FileHistoryList", m_excludeFilesFileHistoryList ); + + config->sync(); +} + +#include "diffsettings.moc" diff --git a/libkomparediff2/diffsettings.h b/libkomparediff2/diffsettings.h new file mode 100644 index 00000000..623c2c83 --- /dev/null +++ b/libkomparediff2/diffsettings.h @@ -0,0 +1,65 @@ +/*************************************************************************** + diffsettings.h + -------------- + begin : Sun Mar 4 2001 + Copyright 2001-2003 Otto Bruggeman + Copyright 2001-2003 John Firebaugh +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 DIFFSETTINGS_H +#define DIFFSETTINGS_H + +#include +#include + +#include "kompare.h" +#include "settingsbase.h" +#include "diff2export.h" + +class DIFF2_EXPORT DiffSettings : public SettingsBase +{ +Q_OBJECT +public: + DiffSettings( QWidget* parent ); + virtual ~DiffSettings(); +public: + // some virtual functions that will be overloaded from the base class + virtual void loadSettings( KConfig* config ); + virtual void saveSettings( KConfig* config ); + +public: + QString m_diffProgram; + int m_linesOfContext; + Kompare::Format m_format; + bool m_largeFiles; // -H + bool m_ignoreWhiteSpace; // -b + bool m_ignoreAllWhiteSpace; // -w + bool m_ignoreEmptyLines; // -B + bool m_ignoreChangesDueToTabExpansion; // -E + bool m_createSmallerDiff; // -d + bool m_ignoreChangesInCase; // -i + bool m_showCFunctionChange; // -p + bool m_convertTabsToSpaces; // -t + bool m_ignoreRegExp; // -I + QString m_ignoreRegExpText; // the RE for -I + QStringList m_ignoreRegExpTextHistory; + bool m_recursive; // -r + bool m_newFiles; // -N +// bool m_allText; // -a + bool m_excludeFilePattern; // -x + QStringList m_excludeFilePatternList; // The list of patterns for -x + bool m_excludeFilesFile; // -X + QString m_excludeFilesFileURL; // The filename to -X + QStringList m_excludeFilesFileHistoryList; // The history list of filenames +}; + +#endif diff --git a/libkomparediff2/kompare.h b/libkomparediff2/kompare.h new file mode 100644 index 00000000..54aae117 --- /dev/null +++ b/libkomparediff2/kompare.h @@ -0,0 +1,174 @@ +/*************************************************************************** + kompare.h - description + ------------------- + begin : Sun Mar 4 2001 + Copyright 2001-2003 Otto Bruggeman + Copyright 2001-2003 John Firebaugh +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 KOMPARE_H +#define KOMPARE_H + +#include + +#include "diff2export.h" + +// Forward declaration needed +class KTempDir; + +namespace Kompare +{ + enum Format { + Context = 0, + Ed = 1, + Normal = 2, + RCS = 3, + Unified = 4, + SideBySide = 5, + UnknownFormat = -1 + }; + + enum Generator { + CVSDiff = 0, + Diff = 1, + Perforce = 2, + SubVersion = 3, + Reserved2 = 4, + Reserved3 = 5, + Reserved4 = 6, + Reserved5 = 7, + Reserved6 = 8, + Reserved7 = 9, + UnknownGenerator = -1 + }; + + enum Mode { + ComparingFiles, // compareFiles + ComparingFileString, // Compare a source file with a destination string + ComparingStringFile, // Compare a source string with a destination file + ComparingDirs, // compareDirs + ShowingDiff, // openDiff + BlendingDir, // openDirAndDiff + BlendingFile, // openFileAndDiff + UnknownMode // Used to initialize the Infoi struct + }; + + enum DiffMode { + Default, + Custom, + UnknownDiffMode // Use to initialize the Info struct + }; + + enum Status { + RunningDiff, + Parsing, + FinishedParsing, + FinishedWritingDiff, + ReRunningDiff // When a change has been detected after diff has run + }; + + enum Target { + Source, + Destination + }; + + struct Info { + Info ( + enum Mode _mode = UnknownMode, + enum DiffMode _diffMode = UnknownDiffMode, + enum Format _format = UnknownFormat, + enum Generator _generator = UnknownGenerator, + KUrl _source = KUrl(), + KUrl _destination = KUrl(), + QString _localSource = "", + QString _localDestination = "", + KTempDir* _sourceKTempDir = 0, + KTempDir* _destinationKTempDir = 0, + uint _depth = 0, + bool _applied = true + ) + { + mode = _mode; + diffMode = _diffMode; + format = _format; + generator = _generator; + source = _source; + destination = _destination; + localSource = _localSource; + localDestination = _localDestination; + sourceKTempDir = _sourceKTempDir; + destinationKTempDir = _destinationKTempDir; + depth = _depth; + applied = _applied; + } + void swapSourceWithDestination() + { + KUrl url = source; + source = destination; + destination = url; + + QString string = localSource; + localSource = localDestination; + localDestination = string; + + KTempDir* tmpDir = sourceKTempDir; + sourceKTempDir = destinationKTempDir; + destinationKTempDir = tmpDir; + } + enum Mode mode; + enum DiffMode diffMode; + enum Format format; + enum Generator generator; + KUrl source; + KUrl destination; + QString localSource; + QString localDestination; + KTempDir* sourceKTempDir; + KTempDir* destinationKTempDir; + uint depth; + bool applied; + }; +} // End of namespace Kompare + +/* +** This should be removed and put somewhere else +*/ +class DIFF2_EXPORT KompareFunctions +{ +public: + static QString constructRelativePath( const QString& from, const QString& to ) + { + KUrl fromURL( from ); + KUrl toURL( to ); + KUrl root; + int upLevels = 0; + + // Find a common root. + root = from; + while( root.isValid() && !root.isParentOf( toURL ) ) { + root = root.upUrl(); + upLevels++; + } + + if( !root.isValid() ) return to; + + QString relative; + for( ; upLevels > 0; upLevels-- ) { + relative += "../"; + } + + relative += QString( to ).replace( 0, root.path( KUrl::LeaveTrailingSlash ).length(), "" ); + return relative; + } +}; + +#endif diff --git a/libkomparediff2/komparemodellist.cpp b/libkomparediff2/komparemodellist.cpp new file mode 100644 index 00000000..faf0617d --- /dev/null +++ b/libkomparediff2/komparemodellist.cpp @@ -0,0 +1,1505 @@ +/*************************************************************************** + komparemodellist.cpp + -------------------- + begin : Tue Jun 26 2001 + Copyright 2001-2005,2009 Otto Bruggeman + Copyright 2001-2003 John Firebaugh + Copyright 2007-2010 Kevin Kofler + Copyright 2012 Jean-Nicolas Artaud + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the 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 "komparemodellist.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "difference.h" +#include "diffhunk.h" +#include "diffmodel.h" +#include "diffmodellist.h" +#include "kompareprocess.h" +#include "parser.h" +#include + +static const KCatalogLoader loader("libkomparediff2"); + +using namespace Diff2; + +KompareModelList::KompareModelList( DiffSettings* diffSettings, QWidget* widgetForKIO, QObject* parent, const char* name, bool isReadWrite) + : QObject( parent ), + m_diffProcess( 0 ), + m_diffSettings( diffSettings ), + m_models( 0 ), + m_selectedModel( 0 ), + m_selectedDifference( 0 ), + m_modelIndex( 0 ), + m_info( 0 ), + m_textCodec( 0 ), + m_widgetForKIO( widgetForKIO ), + m_isReadWrite( isReadWrite ) +{ + kDebug(8101) << "Show me the arguments: " << diffSettings << ", " << widgetForKIO << ", " << parent << ", " << name << endl; + m_actionCollection = new KActionCollection(this); + m_applyDifference = m_actionCollection->addAction( "difference_apply", this, SLOT(slotActionApplyDifference()) ); + m_applyDifference->setIcon( KIcon("arrow-right") ); + m_applyDifference->setText( i18n("&Apply Difference") ); + m_applyDifference->setShortcut( QKeySequence(Qt::Key_Space) ); + m_unApplyDifference = m_actionCollection->addAction( "difference_unapply", this, SLOT(slotActionUnApplyDifference()) ); + m_unApplyDifference->setIcon( KIcon("arrow-left") ); + m_unApplyDifference->setText( i18n("Un&apply Difference") ); + m_unApplyDifference->setShortcut( QKeySequence(Qt::Key_Backspace) ); + m_applyAll = m_actionCollection->addAction( "difference_applyall", this, SLOT(slotActionApplyAllDifferences()) ); + m_applyAll->setIcon( KIcon("arrow-right-double") ); + m_applyAll->setText( i18n("App&ly All") ); + m_applyAll->setShortcut( QKeySequence(Qt::CTRL + Qt::Key_A) ); + m_unapplyAll = m_actionCollection->addAction( "difference_unapplyall", this, SLOT(slotActionUnapplyAllDifferences()) ); + m_unapplyAll->setIcon( KIcon("arrow-left-double") ); + m_unapplyAll->setText( i18n("&Unapply All") ); + m_unapplyAll->setShortcut( QKeySequence(Qt::CTRL + Qt::Key_U) ); + m_previousFile = m_actionCollection->addAction( "difference_previousfile", this, SLOT(slotPreviousModel()) ); + m_previousFile->setIcon( KIcon("arrow-up-double") ); + m_previousFile->setText( i18n("P&revious File") ); + m_previousFile->setShortcut( QKeySequence(Qt::CTRL + Qt::Key_PageUp) ); + m_nextFile = m_actionCollection->addAction( "difference_nextfile", this, SLOT(slotNextModel()) ); + m_nextFile->setIcon( KIcon("arrow-down-double") ); + m_nextFile->setText( i18n("N&ext File") ); + m_nextFile->setShortcut( QKeySequence(Qt::CTRL + Qt::Key_PageDown) ); + m_previousDifference = m_actionCollection->addAction( "difference_previous", this, SLOT(slotPreviousDifference()) ); + m_previousDifference->setIcon( KIcon("arrow-up") ); + m_previousDifference->setText( i18n("&Previous Difference") ); + m_previousDifference->setShortcut( QKeySequence(Qt::CTRL + Qt::Key_Up) ); + m_nextDifference = m_actionCollection->addAction( "difference_next", this, SLOT(slotNextDifference()) ); + m_nextDifference->setIcon( KIcon("arrow-down") ); + m_nextDifference->setText( i18n("&Next Difference") ); + m_nextDifference->setShortcut( QKeySequence(Qt::CTRL + Qt::Key_Down) ); + m_previousDifference->setEnabled( false ); + m_nextDifference->setEnabled( false ); + + m_save = KStandardAction::save( this, SLOT(slotSaveDestination()), m_actionCollection ); + m_save->setEnabled( false ); + + updateModelListActions(); +} + +KompareModelList::~KompareModelList() +{ + m_selectedModel = 0; + m_selectedDifference = 0; + m_info = 0; + delete m_models; +} + +bool KompareModelList::isDirectory( const QString& url ) const +{ + QFileInfo fi( url ); + if ( fi.isDir() ) + return true; + else + return false; +} + +bool KompareModelList::isDiff( const QString& mimeType ) const +{ + if ( mimeType == "text/x-patch" ) + return true; + else + return false; +} + +bool KompareModelList::compare() +{ + bool result = false; + + bool sourceIsDirectory = isDirectory( m_info->localSource ); + bool destinationIsDirectory = isDirectory( m_info->localDestination ); + + if ( sourceIsDirectory && destinationIsDirectory ) + { + m_info->mode = Kompare::ComparingDirs; + result = compare(m_info->mode); + } + else if ( !sourceIsDirectory && !destinationIsDirectory ) + { + QFile sourceFile( m_info->localSource ); + sourceFile.open( QIODevice::ReadOnly ); + QString sourceMimeType = ( KMimeType::findByContent( sourceFile.readAll() ) )->name(); + sourceFile.close(); + kDebug(8101) << "Mimetype source : " << sourceMimeType << endl; + + QFile destinationFile( m_info->localDestination ); + destinationFile.open( QIODevice::ReadOnly ); + QString destinationMimeType = ( KMimeType::findByContent( destinationFile.readAll() ) )->name(); + destinationFile.close(); + kDebug(8101) << "Mimetype destination: " << destinationMimeType << endl; + + // Not checking if it is a text file/something diff can even compare, we'll let diff handle that + if ( !isDiff( sourceMimeType ) && isDiff( destinationMimeType ) ) + { + kDebug(8101) << "Blending destination into source..." << endl; + m_info->mode = Kompare::BlendingFile; + result = openFileAndDiff(); + } + else if ( isDiff( sourceMimeType ) && !isDiff( destinationMimeType ) ) + { + kDebug(8101) << "Blending source into destination..." << endl; + m_info->mode = Kompare::BlendingFile; + // Swap source and destination before calling this + m_info->swapSourceWithDestination(); + // Do we need to notify anyone we swapped source and destination? + // No we do not need to notify anyone about swapping source with destination + result = openFileAndDiff(); + } + else + { + kDebug(8101) << "Comparing source with destination" << endl; + m_info->mode = Kompare::ComparingFiles; + result = compare(m_info->mode); + } + } + else if ( sourceIsDirectory && !destinationIsDirectory ) + { + m_info->mode = Kompare::BlendingDir; + result = openDirAndDiff(); + } + else + { + m_info->mode = Kompare::BlendingDir; + // Swap source and destination first in m_info + m_info->swapSourceWithDestination(); + // Do we need to notify anyone we swapped source and destination? + // No we do not need to notify anyone about swapping source with destination + result = openDirAndDiff(); + } + + return result; +} + +bool KompareModelList::compare(Kompare::Mode mode) +{ + clear(); // Destroy the old models... + + m_diffProcess = new KompareProcess( m_diffSettings, Kompare::Custom, m_info->localSource, m_info->localDestination, QString(), mode ); + m_diffProcess->setEncoding( m_encoding ); + + connect( m_diffProcess, SIGNAL(diffHasFinished( bool )), + this, SLOT(slotDiffProcessFinished( bool )) ); + + emit status( Kompare::RunningDiff ); + m_diffProcess->start(); + + return true; +} + +QString lstripSeparators( const QString & from, uint count ) +{ + int position = 0; + for ( uint i = 0; i < count; ++i ) + { + position = from.indexOf('/', position); + if ( position == -1 ) + { + break; + } + } + if ( position == -1 ) + { + return ""; + } + else + { + return from.mid(position); + } +} + +void KompareModelList::setDepthAndApplied() +{ + // Splice to avoid calling ~DiffModelList + QList splicedModelList(*m_models); + foreach(DiffModel* model, splicedModelList) + { + model->setSourceFile( lstripSeparators(model->source(), m_info->depth) ); + model->setDestinationFile( lstripSeparators(model->destination(), m_info->depth) ); + model->applyAllDifferences(m_info->applied); + } +} + +bool KompareModelList::openFileAndDiff() +{ + clear(); + + if ( parseDiffOutput( readFile( m_info->localDestination ) ) != 0 ) + { + emit error( i18n( "No models or no differences, this file: %1, is not a valid diff file.", m_info->destination.url() ) ); + return false; + } + + setDepthAndApplied(); + + if ( !blendOriginalIntoModelList( m_info->localSource ) ) + { + kDebug(8101) << "Oops cant blend original file into modellist : " << m_info->localSource << endl; + emit( i18n( "There were problems applying the diff %1 to the file %2.", m_info->destination.url(), m_info->source.url() ) ); + return false; + } + + updateModelListActions(); + show(); + + return true; +} + +bool KompareModelList::openDirAndDiff() +{ + clear(); + + if ( parseDiffOutput( readFile( m_info->localDestination ) ) != 0 ) + { + emit error( i18n( "No models or no differences, this file: %1, is not a valid diff file.", m_info->destination.url() ) ); + return false; + } + + setDepthAndApplied(); + + // Do our thing :) + if ( !blendOriginalIntoModelList( m_info->localSource ) ) + { + // Trouble blending the original into the model + kDebug(8101) << "Oops cant blend original dir into modellist : " << m_info->localSource << endl; + emit error( i18n( "There were problems applying the diff %1 to the folder %2.", m_info->destination.url(), m_info->source.url() ) ); + return false; + } + + updateModelListActions(); + show(); + + return true; +} + +void KompareModelList::slotSaveDestination() +{ + // Unnecessary safety check! We can now guarantee that saving is only possible when there is a model and there are unsaved changes + if ( m_selectedModel ) + { + saveDestination( m_selectedModel ); + m_save->setEnabled( false ); + emit updateActions(); + } +} + +bool KompareModelList::saveDestination( DiffModel* model ) +{ + kDebug(8101) << "KompareModelList::saveDestination: " << endl; + + // Unecessary safety check, we can guarantee there are unsaved changes!!! + if( !model->hasUnsavedChanges() ) + return true; + + KTemporaryFile temp; + + if( !temp.open() ) { + emit error( i18n( "Could not open a temporary file." ) ); + temp.remove(); + return false; + } + + QTextStream stream( &temp ); + QStringList list; + + DiffHunkListConstIterator hunkIt = model->hunks()->constBegin(); + DiffHunkListConstIterator hEnd = model->hunks()->constEnd(); + + for( ; hunkIt != hEnd; ++hunkIt ) + { + DiffHunk* hunk = *hunkIt; + + DifferenceListConstIterator diffIt = hunk->differences().constBegin(); + DifferenceListConstIterator dEnd = hunk->differences().constEnd(); + + Difference* diff; + for( ; diffIt != dEnd; ++diffIt ) + { + diff = *diffIt; + if( !diff->applied() ) + { + DifferenceStringListConstIterator stringIt = diff->destinationLines().begin(); + DifferenceStringListConstIterator sEnd = diff->destinationLines().end(); + for ( ; stringIt != sEnd; ++stringIt ) + { + list.append( ( *stringIt )->string() ); + } + } + else + { + DifferenceStringListConstIterator stringIt = diff->sourceLines().begin(); + DifferenceStringListConstIterator sEnd = diff->sourceLines().end(); + for ( ; stringIt != sEnd; ++stringIt ) + { + list.append( ( *stringIt )->string() ); + } + } + } + } + + // kDebug(8101) << "Everything: " << endl << list.join( "\n" ) << endl; + + if( list.count() > 0 ) + stream << list.join( "" ); + if( temp.error() != QFile::NoError ) { + emit error( i18n( "Could not write to the temporary file %1, deleting it.", temp.fileName() ) ); + temp.remove(); + return false; + } + + temp.close(); + if( temp.error() != QFile::NoError ) { + emit error( i18n( "Could not write to the temporary file %1, deleting it.", temp.fileName() ) ); + temp.remove(); + return false; + } + + bool result = false; + + // Make sure the destination directory exists, it is possible when using -N to not have the destination dir/file available + if ( m_info->mode == Kompare::ComparingDirs ) + { + // Dont use destination which was used for creating the diff directly, use the original URL!!! + // FIXME!!! Wrong destination this way! Need to add the sub directory to the url!!!! + kDebug(8101) << "Tempfilename (save) : " << temp.fileName() << endl; + kDebug(8101) << "Model->path+file : " << model->destinationPath() << model->destinationFile() << endl; + kDebug(8101) << "info->localdest : " << m_info->localDestination << endl; + QString tmp = model->destinationPath() + model->destinationFile(); + if ( tmp.startsWith( m_info->localDestination ) ) // It should, if not serious trouble... + tmp.remove( 0, m_info->localDestination.size() ); + kDebug(8101) << "DestinationURL : " << m_info->destination << endl; + kDebug(8101) << "tmp : " << tmp << endl; + KIO::UDSEntry entry; + KUrl fullDestinationPath( m_info->destination ); + fullDestinationPath.addPath( tmp ); + kDebug(8101) << "fullDestinationPath : " << fullDestinationPath << endl; + if ( !KIO::NetAccess::stat( fullDestinationPath.directory(), entry, m_widgetForKIO ) ) + { + if ( !KIO::NetAccess::mkdir( fullDestinationPath.directory(), m_widgetForKIO ) ) + { + emit error( i18n( "Could not create destination directory %1.\nThe file has not been saved.", fullDestinationPath.directory() ) ); + return false; + } + } + result = KIO::NetAccess::upload( temp.fileName(), fullDestinationPath, m_widgetForKIO ); + } + else + { + kDebug(8101) << "Tempfilename : " << temp.fileName() << endl; + kDebug(8101) << "DestinationURL : " << m_info->destination << endl; + result = KIO::NetAccess::upload( temp.fileName(), m_info->destination, m_widgetForKIO ); + kDebug(8101) << "true or false?" << result << endl; + } + + if ( !result ) + { + // FIXME: Wrong first argument given in case of comparing directories! + emit error( i18n( "Could not upload the temporary file to the destination location %1. The temporary file is still available under: %2. You can manually copy it to the right place.", m_info->destination.url(), temp.fileName() ) ); + //Don't remove file when we delete temp and don't leak it. + temp.setAutoRemove(false); + } + else + { + temp.remove(); + } + + // If saving was fine set all differences to saved so we can start again with a clean slate + if ( result ) + { + DifferenceListConstIterator diffIt = model->differences()->constBegin(); + DifferenceListConstIterator endIt = model->differences()->constEnd(); + + for (; diffIt != endIt; ++diffIt ) + { + (*diffIt)->setUnsaved( false ); + } + } + + return true; +} + +bool KompareModelList::saveAll() +{ + if ( modelCount() == 0 ) + return false; + + DiffModelListIterator it = m_models->begin(); + DiffModelListIterator end = m_models->end(); + for ( ; it != end; ++it ) + { + if( !saveDestination( *it ) ) + return false; + } + + return true; +} + +void KompareModelList::setEncoding( const QString& encoding ) +{ + m_encoding = encoding; + if ( !encoding.compare( "default", Qt::CaseInsensitive ) ) + { + m_textCodec = QTextCodec::codecForLocale(); + } + else + { + kDebug(8101) << "Encoding : " << encoding << endl; + m_textCodec = KGlobal::charsets()->codecForName( encoding.toLatin1() ); + kDebug(8101) << "TextCodec: " << m_textCodec << endl; + if ( !m_textCodec ) + m_textCodec = QTextCodec::codecForLocale(); + } + kDebug(8101) << "TextCodec: " << m_textCodec << endl; +} + +void KompareModelList::slotDiffProcessFinished( bool success ) +{ + if ( success ) + { + emit status( Kompare::Parsing ); + if ( parseDiffOutput( m_diffProcess->diffOutput() ) != 0 ) + { + emit error( i18n( "Could not parse diff output." ) ); + } + else + { + if ( m_info->mode != Kompare::ShowingDiff ) + { + kDebug(8101) << "Blend this crap please and do not give me any conflicts..." << endl; + blendOriginalIntoModelList( m_info->localSource ); + } + updateModelListActions(); + show(); + } + emit status( Kompare::FinishedParsing ); + } + else if ( m_diffProcess->exitStatus() == 0 ) + { + emit error( i18n( "The files are identical." ) ); + } + else + { + emit error( m_diffProcess->stdErr() ); + } + + m_diffProcess->deleteLater(); + m_diffProcess = 0; +} + +void KompareModelList::slotDirectoryChanged( const QString& /*dir*/ ) +{ + // some debug output to see if watching works properly + kDebug(8101) << "Yippie directories are being watched !!! :)" << endl; + if ( m_diffProcess ) + { + emit status( Kompare::ReRunningDiff ); + m_diffProcess->start(); + } +} + +void KompareModelList::slotFileChanged( const QString& /*file*/ ) +{ + // some debug output to see if watching works properly + kDebug(8101) << "Yippie files are being watched !!! :)" << endl; + if ( m_diffProcess ) + { + emit status( Kompare::ReRunningDiff ); + m_diffProcess->start(); + } +} + +QStringList KompareModelList::split( const QString& fileContents ) +{ + QString contents = fileContents; + QStringList list; + + int pos = 0; + int oldpos = 0; + // split that does not strip the split char +#ifdef QT_OS_MAC + const char split = '\r'; +#else + const char split = '\n'; +#endif + while ( ( pos = contents.indexOf( split, oldpos ) ) >= 0 ) + { + list.append( contents.mid( oldpos, pos - oldpos + 1 ) ); + oldpos = pos + 1; + } + + if ( contents.length() > oldpos ) + { + list.append( contents.right( contents.length() - oldpos ) ); + } + + return list; +} + +QString KompareModelList::readFile( const QString& fileName ) +{ + QStringList list; + + QFile file( fileName ); + file.open( QIODevice::ReadOnly ); + + QTextStream stream( &file ); + kDebug(8101) << "Codec = " << m_textCodec << endl; + + if ( !m_textCodec ) + m_textCodec = QTextCodec::codecForLocale(); + + stream.setCodec( m_textCodec ); + + QString contents = stream.readAll(); + + file.close(); + + return contents; +} + +bool KompareModelList::openDiff( const QString& diffFile ) +{ + kDebug(8101) << "Stupid :) Url = " << diffFile << endl; + + if ( diffFile.isEmpty() ) + return false; + + QString diff = readFile( diffFile ); + + clear(); // Clear the current models + + emit status( Kompare::Parsing ); + + if ( parseDiffOutput( diff ) != 0 ) + { + emit error( i18n( "Could not parse diff output." ) ); + return false; + } + + updateModelListActions(); + show(); + + emit status( Kompare::FinishedParsing ); + + return true; +} + +bool KompareModelList::parseAndOpenDiff(const QString& diff) +{ + clear(); // Clear the current models + + emit status( Kompare::Parsing ); + + if ( parseDiffOutput( diff ) != 0 ) + { + emit error( i18n( "Could not parse diff output." ) ); + return false; + } + + updateModelListActions(); + show(); + + emit status( Kompare::FinishedParsing ); + return true; +} + +QString KompareModelList::recreateDiff() const +{ + QString diff; + + DiffModelListConstIterator modelIt = m_models->constBegin(); + DiffModelListConstIterator mEnd = m_models->constEnd(); + + for ( ; modelIt != mEnd; ++modelIt ) + { + diff += (*modelIt)->recreateDiff(); + } + return diff; +} + +bool KompareModelList::saveDiff( const QString& url, QString directory, DiffSettings* diffSettings ) +{ + kDebug(8101) << "KompareModelList::saveDiff: " << endl; + + m_diffTemp = new KTemporaryFile(); + m_diffURL = url; + + if( !m_diffTemp->open() ) { + emit error( i18n( "Could not open a temporary file." ) ); + m_diffTemp->remove(); + delete m_diffTemp; + m_diffTemp = 0; + return false; + } + + m_diffProcess = new KompareProcess( diffSettings, Kompare::Custom, m_info->localSource, m_info->localDestination, directory ); + m_diffProcess->setEncoding( m_encoding ); + + connect( m_diffProcess, SIGNAL(diffHasFinished( bool )), + this, SLOT(slotWriteDiffOutput( bool )) ); + + emit status( Kompare::RunningDiff ); + m_diffProcess->start(); + return true; +} + +void KompareModelList::slotWriteDiffOutput( bool success ) +{ + kDebug(8101) << "Success = " << success << endl; + + if( success ) + { + QTextStream stream( m_diffTemp ); + + stream << m_diffProcess->diffOutput(); + + m_diffTemp->close(); + + if( false /*|| m_diffTemp->status() != 0 */) + { + emit error( i18n( "Could not write to the temporary file." ) ); + } + + KIO::NetAccess::upload( m_diffTemp->fileName(), KUrl( m_diffURL ), m_widgetForKIO ); + + emit status( Kompare::FinishedWritingDiff ); + } + + m_diffURL.truncate( 0 ); + m_diffTemp->remove(); + + delete m_diffTemp; + m_diffTemp = 0; + + delete m_diffProcess; + m_diffProcess = 0; +} + +void KompareModelList::slotSelectionChanged( const Diff2::DiffModel* model, const Diff2::Difference* diff ) +{ +// This method will signal all the other objects about a change in selection, +// it will emit setSelection( const DiffModel*, const Difference* ) to all who are connected + kDebug(8101) << "KompareModelList::slotSelectionChanged( " << model << ", " << diff << " )" << endl; + kDebug(8101) << "Sender is : " << sender()->metaObject()->className() << endl; +// kDebug(8101) << kBacktrace() << endl; + + m_selectedModel = const_cast(model); + m_modelIndex = m_models->indexOf( m_selectedModel ); + kDebug(8101) << "m_modelIndex = " << m_modelIndex << endl; + m_selectedDifference = const_cast(diff); + + m_selectedModel->setSelectedDifference( m_selectedDifference ); + + // setSelected* search for the argument in the lists and return false if not found + // if found they return true and set the m_selected* + if ( !setSelectedModel( m_selectedModel ) ) + { + // Backup plan + m_selectedModel = firstModel(); + m_selectedDifference = m_selectedModel->firstDifference(); + } + else if ( !m_selectedModel->setSelectedDifference( m_selectedDifference ) ) + { + // Another backup plan + m_selectedDifference = m_selectedModel->firstDifference(); + } + + emit setSelection( model, diff ); + emit setStatusBarModelInfo( findModel( m_selectedModel ), m_selectedModel->findDifference( m_selectedDifference ), modelCount(), differenceCount(), m_selectedModel->appliedCount() ); + + updateModelListActions(); +} + +void KompareModelList::slotSelectionChanged( const Diff2::Difference* diff ) +{ +// This method will emit setSelection( const Difference* ) to whomever is listening +// when for instance in kompareview the selection has changed + kDebug(8101) << "KompareModelList::slotSelectionChanged( " << diff << " )" << endl; + kDebug(8101) << "Sender is : " << sender()->metaObject()->className() << endl; + + m_selectedDifference = const_cast(diff); + + if ( !m_selectedModel->setSelectedDifference( m_selectedDifference ) ) + { + // Backup plan + m_selectedDifference = m_selectedModel->firstDifference(); + } + + emit setSelection( diff ); + emit setStatusBarModelInfo( findModel( m_selectedModel ), m_selectedModel->findDifference( m_selectedDifference ), modelCount(), differenceCount(), m_selectedModel->appliedCount() ); + + updateModelListActions(); +} + +void KompareModelList::slotPreviousModel() +{ + if ( ( m_selectedModel = prevModel() ) != 0 ) + { + m_selectedDifference = m_selectedModel->firstDifference(); + } + else + { + m_selectedModel = firstModel(); + m_selectedDifference = m_selectedModel->firstDifference(); + } + + emit setSelection( m_selectedModel, m_selectedDifference ); + emit setStatusBarModelInfo( findModel( m_selectedModel ), m_selectedModel->findDifference( m_selectedDifference ), modelCount(), differenceCount(), m_selectedModel->appliedCount() ); + updateModelListActions(); +} + +void KompareModelList::slotNextModel() +{ + if ( ( m_selectedModel = nextModel() ) != 0 ) + { + m_selectedDifference = m_selectedModel->firstDifference(); + } + else + { + m_selectedModel = lastModel(); + m_selectedDifference = m_selectedModel->firstDifference(); + } + + emit setSelection( m_selectedModel, m_selectedDifference ); + emit setStatusBarModelInfo( findModel( m_selectedModel ), m_selectedModel->findDifference( m_selectedDifference ), modelCount(), differenceCount(), m_selectedModel->appliedCount() ); + updateModelListActions(); +} + +DiffModel* KompareModelList::firstModel() +{ + kDebug(8101) << "KompareModelList::firstModel()" << endl; + m_modelIndex = 0; + kDebug(8101) << "m_modelIndex = " << m_modelIndex << endl; + + m_selectedModel = m_models->first(); + + return m_selectedModel; +} + +DiffModel* KompareModelList::lastModel() +{ + kDebug(8101) << "KompareModelList::lastModel()" << endl; + m_modelIndex = m_models->count() - 1; + kDebug(8101) << "m_modelIndex = " << m_modelIndex << endl; + + m_selectedModel = m_models->last(); + + return m_selectedModel; +} + +DiffModel* KompareModelList::prevModel() +{ + kDebug(8101) << "KompareModelList::prevModel()" << endl; + if ( m_modelIndex > 0 && --m_modelIndex < m_models->count() ) + { + kDebug(8101) << "m_modelIndex = " << m_modelIndex << endl; + m_selectedModel = (*m_models)[ m_modelIndex ]; + } + else + { + m_selectedModel = 0; + m_modelIndex = 0; + kDebug(8101) << "m_modelIndex = " << m_modelIndex << endl; + } + + return m_selectedModel; +} + +DiffModel* KompareModelList::nextModel() +{ + kDebug(8101) << "KompareModelList::nextModel()" << endl; + if ( ++m_modelIndex < m_models->count() ) + { + kDebug(8101) << "m_modelIndex = " << m_modelIndex << endl; + m_selectedModel = (*m_models)[ m_modelIndex ]; + } + else + { + m_selectedModel = 0; + m_modelIndex = 0; + kDebug(8101) << "m_modelIndex = " << m_modelIndex << endl; + } + + return m_selectedModel; +} + +KActionCollection* KompareModelList::actionCollection() const +{ + return m_actionCollection; +} + +void KompareModelList::slotPreviousDifference() +{ + kDebug(8101) << "slotPreviousDifference called" << endl; + if ( ( m_selectedDifference = m_selectedModel->prevDifference() ) != 0 ) + { + emit setSelection( m_selectedDifference ); + emit setStatusBarModelInfo( findModel( m_selectedModel ), m_selectedModel->findDifference( m_selectedDifference ), modelCount(), differenceCount(), m_selectedModel->appliedCount() ); + updateModelListActions(); + return; + } + + kDebug(8101) << "**** no previous difference... ok lets find the previous model..." << endl; + + if ( ( m_selectedModel = prevModel() ) != 0 ) + { + m_selectedDifference = m_selectedModel->lastDifference(); + + emit setSelection( m_selectedModel, m_selectedDifference ); + emit setStatusBarModelInfo( findModel( m_selectedModel ), m_selectedModel->findDifference( m_selectedDifference ), modelCount(), differenceCount(), m_selectedModel->appliedCount() ); + updateModelListActions(); + return; + } + + + kDebug(8101) << "**** !!! No previous model, ok backup plan activated..." << endl; + + // Backup plan + m_selectedModel = firstModel(); + m_selectedDifference = m_selectedModel->firstDifference(); + + emit setSelection( m_selectedModel, m_selectedDifference ); + emit setStatusBarModelInfo( findModel( m_selectedModel ), m_selectedModel->findDifference( m_selectedDifference ), modelCount(), differenceCount(), m_selectedModel->appliedCount() ); + updateModelListActions(); +} + +void KompareModelList::slotNextDifference() +{ + kDebug(8101) << "slotNextDifference called" << endl; + if ( ( m_selectedDifference = m_selectedModel->nextDifference() ) != 0 ) + { + emit setSelection( m_selectedDifference ); + emit setStatusBarModelInfo( findModel( m_selectedModel ), m_selectedModel->findDifference( m_selectedDifference ), modelCount(), differenceCount(), m_selectedModel->appliedCount() ); + updateModelListActions(); + return; + } + + kDebug(8101) << "**** no next difference... ok lets find the next model..." << endl; + + if ( ( m_selectedModel = nextModel() ) != 0 ) + { + m_selectedDifference = m_selectedModel->firstDifference(); + + emit setSelection( m_selectedModel, m_selectedDifference ); + emit setStatusBarModelInfo( findModel( m_selectedModel ), m_selectedModel->findDifference( m_selectedDifference ), modelCount(), differenceCount(), m_selectedModel->appliedCount() ); + updateModelListActions(); + return; + } + + kDebug(8101) << "**** !!! No next model, ok backup plan activated..." << endl; + + // Backup plan + m_selectedModel = lastModel(); + m_selectedDifference = m_selectedModel->lastDifference(); + + emit setSelection( m_selectedModel, m_selectedDifference ); + emit setStatusBarModelInfo( findModel( m_selectedModel ), m_selectedModel->findDifference( m_selectedDifference ), modelCount(), differenceCount(), m_selectedModel->appliedCount() ); + updateModelListActions(); +} + +void KompareModelList::slotApplyDifference( bool apply ) +{ + m_selectedModel->applyDifference( apply ); + emit applyDifference( apply ); +} + +void KompareModelList::slotApplyAllDifferences( bool apply ) +{ + m_selectedModel->applyAllDifferences( apply ); + emit applyAllDifferences( apply ); +} + +int KompareModelList::parseDiffOutput( const QString& diff ) +{ + kDebug(8101) << "KompareModelList::parseDiffOutput" << endl; + emit diffString( diff ); + + QStringList diffLines = split( diff ); + + Parser* parser = new Parser( this ); + bool malformed = false; + m_models = parser->parse( diffLines, &malformed ); + + m_info->generator = parser->generator(); + m_info->format = parser->format(); + + delete parser; + + if ( m_models ) + { + if ( malformed ) + { + kDebug(8101) << "Malformed diff" << endl; + emit error( i18n( "The diff is malformed. Some lines could not be parsed and will not be displayed in the diff view." ) ); + // proceed anyway with the lines which have been parsed + } + m_selectedModel = firstModel(); + kDebug(8101) << "Ok there are differences..." << endl; + m_selectedDifference = m_selectedModel->firstDifference(); + emit setStatusBarModelInfo( 0, 0, modelCount(), differenceCount(), 0); + } + else + { + // Wow trouble, no models, so no differences... + kDebug(8101) << "Now i'll be damned, there should be models here !!!" << endl; + return -1; + } + + return 0; +} + +bool KompareModelList::blendOriginalIntoModelList( const QString& localURL ) +{ + kDebug(8101) << "Hurrah we are blending..." << endl; + QFileInfo fi( localURL ); + + bool result = false; + DiffModel* model; + + QString fileContents; + + if ( fi.isDir() ) + { // is a dir + kDebug(8101) << "Blend Dir" << endl; +// QDir dir( localURL, QString(), QDir::Name|QDir::DirsFirst, QDir::TypeMask ); + DiffModelListIterator modelIt = m_models->begin(); + DiffModelListIterator mEnd = m_models->end(); + for ( ; modelIt != mEnd; ++modelIt ) + { + model = *modelIt; + kDebug(8101) << "Model : " << model << endl; + QString filename = model->source(); + if ( !filename.startsWith( localURL ) ) + filename = QDir(localURL).filePath(filename); + QFileInfo fi2( filename ); + if ( fi2.exists() ) + { + kDebug(8101) << "Reading from: " << filename << endl; + fileContents = readFile( filename ); + result = blendFile( model, fileContents ); + } + else + { + kDebug(8101) << "File " << filename << " does not exist !" << endl; + kDebug(8101) << "Assume empty file !" << endl; + fileContents.truncate( 0 ); + result = blendFile( model, fileContents ); + } + } + kDebug(8101) << "End of Blend Dir" << endl; + } + else if ( fi.isFile() ) + { // is a file + kDebug(8101) << "Blend File" << endl; + kDebug(8101) << "Reading from: " << localURL << endl; + fileContents = readFile( localURL ); + + result = blendFile( (*m_models)[ 0 ], fileContents ); + kDebug(8101) << "End of Blend File" << endl; + } + + return result; +} + +bool KompareModelList::blendFile( DiffModel* model, const QString& fileContents ) +{ + if ( !model ) + { + kDebug(8101) << "**** model is null :(" << endl; + return false; + } + + model->setBlended( true ); + + int srcLineNo = 1, destLineNo = 1; + + QStringList list = split( fileContents ); + QLinkedList lines; + foreach (const QString &str, list) { + lines.append(str); + } + + QLinkedList::ConstIterator linesIt = lines.begin(); + QLinkedList::ConstIterator lEnd = lines.end(); + + DiffHunkList* hunks = model->hunks(); + kDebug(8101) << "Hunks in hunklist: " << hunks->count() << endl; + DiffHunkListIterator hunkIt = hunks->begin(); + + DiffHunk* newHunk = 0; + Difference* newDiff = 0; + + // FIXME: this approach is not very good, we should first check if the hunk applies cleanly + // and without offset and if not use that new linenumber with offset to compare against + // This will only work for files we just diffed with kompare but not for blending where + // file(s) to patch has/have potentially changed + + for ( ; hunkIt != hunks->end(); ++hunkIt ) + { + // Do we need to insert a new hunk before this one ? + DiffHunk* hunk = *hunkIt; + if ( srcLineNo < hunk->sourceLineNumber() ) + { + newHunk = new DiffHunk( srcLineNo, destLineNo, "", DiffHunk::AddedByBlend ); + + hunkIt = ++hunks->insert( hunkIt, newHunk ); + + newDiff = new Difference( srcLineNo, destLineNo, + Difference::Unchanged ); + + newHunk->add( newDiff ); + + while ( srcLineNo < hunk->sourceLineNumber() && linesIt != lEnd ) + { + newDiff->addSourceLine( *linesIt ); + newDiff->addDestinationLine( *linesIt ); + srcLineNo++; + destLineNo++; + ++linesIt; + } + } + + // Now we add the linecount difference for the hunk that follows + int size = hunk->sourceLineCount(); + + for ( int i = 0; i < size; ++i ) + { + ++linesIt; + } + + srcLineNo += size; + destLineNo += hunk->destinationLineCount(); + } + + if ( linesIt != lEnd ) + { + newHunk = new DiffHunk( srcLineNo, destLineNo, "", DiffHunk::AddedByBlend ); + + model->addHunk( newHunk ); + + newDiff = new Difference( srcLineNo, destLineNo, Difference::Unchanged ); + + newHunk->add( newDiff ); + + while ( linesIt != lEnd ) + { + newDiff->addSourceLine( *linesIt ); + newDiff->addDestinationLine( *linesIt ); + ++linesIt; + } + } +#if 0 + DifferenceList hunkDiffList = (*hunkIt)->differences(); + DifferenceListIterator diffIt = hunkDiffList.begin(); + DifferenceListIterator dEnd = hunkDiffList.end(); + kDebug(8101) << "Number of differences in hunkDiffList = " << diffList.count() << endl; + + DifferenceListIterator tempIt; + Difference* diff; + + for ( ; diffIt != dEnd; ++diffIt ) + { + diff = *diffIt; + kDebug(8101) << "*(Diff it) = " << diff << endl; + // Check if there are lines in the original file before the difference + // that are not yet in the diff. If so create new Unchanged diff + if ( srcLineNo < diff->sourceLineNumber() ) + { + newDiff = new Difference( srcLineNo, destLineNo, + Difference::Unchanged | Difference::AddedByBlend ); + newHunk->add( newDiff ); + while ( srcLineNo < diff->sourceLineNumber() && linesIt != lEnd ) + { +// kDebug(8101) << "SourceLine = " << srcLineNo << ": " << *linesIt << endl; + newDiff->addSourceLine( *linesIt ); + newDiff->addDestinationLine( *linesIt ); + srcLineNo++; + destLineNo++; + ++linesIt; + } + } + // Now i've got to add that diff + switch ( diff->type() ) + { + case Difference::Unchanged: + kDebug(8101) << "Unchanged" << endl; + for ( int i = 0; i < diff->sourceLineCount(); i++ ) + { + if ( linesIt != lEnd && *linesIt != diff->sourceLineAt( i )->string() ) + { + kDebug(8101) << "Conflict: SourceLine = " << srcLineNo << ": " << *linesIt << endl; + kDebug(8101) << "Conflict: DiffLine = " << diff->sourceLineNumber() + i << ": " << diff->sourceLineAt( i )->string() << endl; + + // Do conflict resolution (well sort of) + diff->sourceLineAt( i )->setConflictString( *linesIt ); + diff->setConflict( true ); + } +// kDebug(8101) << "SourceLine = " << srcLineNo << ": " << *linesIt << endl; +// kDebug(8101) << "DiffLine = " << diff->sourceLineNumber() + i << ": " << diff->sourceLineAt( i )->string() << endl; + srcLineNo++; + destLineNo++; + ++linesIt; + } + + tempIt = diffIt; + --diffIt; + diffList.remove( tempIt ); + newHunk->add( diff ); + + break; + case Difference::Change: + kDebug(8101) << "Change" << endl; + + //QStringListConstIterator saveIt = linesIt; + + for ( int i = 0; i < diff->sourceLineCount(); i++ ) + { + if ( linesIt != lEnd && *linesIt != diff->sourceLineAt( i )->string() ) + { + kDebug(8101) << "Conflict: SourceLine = " << srcLineNo << ": " << *linesIt << endl; + kDebug(8101) << "Conflict: DiffLine = " << diff->sourceLineNumber() + i << ": " << diff->sourceLineAt( i )->string() << endl; + + // Do conflict resolution (well sort of) + diff->sourceLineAt( i )->setConflictString( *linesIt ); + diff->setConflict( true ); + } + srcLineNo++; + destLineNo++; + ++linesIt; + } + + destLineNo += diff->destinationLineCount(); + + tempIt = diffIt; + --diffIt; + diffList.remove( tempIt ); + newHunk->add( diff ); + newModel->addDiff( diff ); + + break; + case Difference::Insert: + kDebug(8101) << "Insert" << endl; + destLineNo += diff->destinationLineCount(); + tempIt = diffIt; + --diffIt; + diffList.remove( tempIt ); + newHunk->add( diff ); + newModel->addDiff( diff ); + break; + case Difference::Delete: + kDebug(8101) << "Delete" << endl; + kDebug(8101) << "Number of lines in Delete: " << diff->sourceLineCount() << endl; + for ( int i = 0; i < diff->sourceLineCount(); i++ ) + { + if ( linesIt != lEnd && *linesIt != diff->sourceLineAt( i )->string() ) + { + kDebug(8101) << "Conflict: SourceLine = " << srcLineNo << ": " << *linesIt << endl; + kDebug(8101) << "Conflict: DiffLine = " << diff->sourceLineNumber() + i << ": " << diff->sourceLineAt( i )->string() << endl; + + // Do conflict resolution (well sort of) + diff->sourceLineAt( i )->setConflictString( *linesIt ); + diff->setConflict( true ); + } + +// kDebug(8101) << "SourceLine = " << srcLineNo << ": " << *it << endl; +// kDebug(8101) << "DiffLine = " << diff->sourceLineNumber() + i << ": " << diff->sourceLineAt( i )->string() << endl; + srcLineNo++; + ++linesIt; + } + + tempIt = diffIt; + --diffIt; + diffList.remove( tempIt ); + newHunk->add( diff ); + newModel->addDiff( diff ); + break; + default: + kDebug(8101) << "****, some diff type we do not know about ???" << endl; + } + } + } +#endif + +/* + diffList = newModel->differences(); + + diff = diffList.first(); + kDebug(8101) << "Count = " << diffList.count() << endl; + for ( diff = diffList.first(); diff; diff = diffList.next() ) + { + kDebug(8101) << "sourcelinenumber = " << diff->sourceLineNumber() << endl; + } +*/ + + m_selectedModel = firstModel(); + + m_selectedDifference = m_selectedModel->firstDifference(); + + return true; +} + +void KompareModelList::show() +{ + kDebug(8101) << "KompareModelList::Show Number of models = " << m_models->count() << endl; + emit modelsChanged( m_models ); + emit setSelection( m_selectedModel, m_selectedDifference ); +} + +void KompareModelList::clear() +{ + if ( m_models ) + m_models->clear(); + + emit modelsChanged( m_models ); +} + +void KompareModelList::refresh() +{ + // FIXME: I can imagine blending also wants to be refreshed so make a switch case here + compare(m_info->mode); +} + +void KompareModelList::swap() +{ + //FIXME Not sure if any mode could be swapped + if ( m_info->mode == Kompare::ComparingFiles ) + compare(m_info->mode); + else if ( m_info->mode == Kompare::ComparingDirs ) + compare(m_info->mode); +} + +bool KompareModelList::hasUnsavedChanges() const +{ + if ( modelCount() == 0 ) + return false; + + DiffModelListConstIterator modelIt = m_models->constBegin(); + DiffModelListConstIterator endIt = m_models->constEnd(); + + for ( ; modelIt != endIt; ++modelIt ) + { + if ( (*modelIt)->hasUnsavedChanges() ) + return true; + } + return false; +} + +int KompareModelList::modelCount() const +{ + return m_models ? m_models->count() : 0; +} + +int KompareModelList::differenceCount() const +{ + return m_selectedModel ? m_selectedModel->differenceCount() : -1; +} + +int KompareModelList::appliedCount() const +{ + return m_selectedModel ? m_selectedModel->appliedCount() : -1; +} + +void KompareModelList::slotKompareInfo( struct Kompare::Info* info ) +{ + m_info = info; +} + +bool KompareModelList::setSelectedModel( DiffModel* model ) +{ + kDebug(8101) << "KompareModelList::setSelectedModel( " << model << " )" << endl; + + if ( model != m_selectedModel ) + { + if ( !m_models->contains( model ) ) + return false; + kDebug(8101) << "m_selectedModel (was) = " << m_selectedModel << endl; + m_modelIndex = m_models->indexOf( model ); + kDebug(8101) << "m_selectedModel (is) = " << m_selectedModel << endl; + m_selectedModel = model; + } + + updateModelListActions(); + + return true; +} + +void KompareModelList::updateModelListActions() +{ + if ( m_models && m_selectedModel && m_selectedDifference ) + { + if ( m_isReadWrite ) + { + if ( m_selectedModel->appliedCount() != m_selectedModel->differenceCount() ) + m_applyAll->setEnabled( true ); + else + m_applyAll->setEnabled( false ); + + if ( m_selectedModel->appliedCount() != 0 ) + m_unapplyAll->setEnabled( true ); + else + m_unapplyAll->setEnabled( false ); + + m_applyDifference->setEnabled( m_selectedDifference->applied() == false ); + m_unApplyDifference->setEnabled( m_selectedDifference->applied() == true ); + m_save->setEnabled( m_selectedModel->hasUnsavedChanges() ); + } + else + { + m_applyDifference->setEnabled ( false ); + m_unApplyDifference->setEnabled( false ); + m_applyAll->setEnabled ( false ); + m_unapplyAll->setEnabled ( false ); + m_save->setEnabled ( false ); + } + + m_previousFile->setEnabled ( hasPrevModel() ); + m_nextFile->setEnabled ( hasNextModel() ); + m_previousDifference->setEnabled( hasPrevDiff() ); + m_nextDifference->setEnabled ( hasNextDiff() ); + } + else + { + m_applyDifference->setEnabled ( false ); + m_unApplyDifference->setEnabled ( false ); + m_applyAll->setEnabled ( false ); + m_unapplyAll->setEnabled ( false ); + + m_previousFile->setEnabled ( false ); + m_nextFile->setEnabled ( false ); + m_previousDifference->setEnabled( false ); + m_nextDifference->setEnabled ( false ); + m_save->setEnabled ( false ); + } +} + +bool KompareModelList::hasPrevModel() const +{ + kDebug(8101) << "KompareModelList::hasPrevModel()" << endl; + + if ( m_modelIndex > 0 ) + { +// kDebug(8101) << "has prev model" << endl; + return true; + } + +// kDebug(8101) << "doesn't have a prev model, this is the first one..." << endl; + + return false; +} + +bool KompareModelList::hasNextModel() const +{ + kDebug(8101) << "KompareModelList::hasNextModel()" << endl; + + if ( m_modelIndex < ( m_models->count() - 1 ) ) + { +// kDebug(8101) << "has next model" << endl; + return true; + } + +// kDebug(8101) << "doesn't have a next model, this is the last one..." << endl; + return false; +} + +bool KompareModelList::hasPrevDiff() const +{ +// kDebug(8101) << "KompareModelList::hasPrevDiff()" << endl; + int index = m_selectedModel->diffIndex(); + + if ( index > 0 ) + { +// kDebug(8101) << "has prev difference in same model" << endl; + return true; + } + + if ( hasPrevModel() ) + { +// kDebug(8101) << "has prev difference but in prev model" << endl; + return true; + } + +// kDebug(8101) << "doesn't have a prev difference, not even in the previous model because there is no previous model" << endl; + + return false; +} + +bool KompareModelList::hasNextDiff() const +{ +// kDebug(8101) << "KompareModelList::hasNextDiff()" << endl; + int index = m_selectedModel->diffIndex(); + + if ( index < ( m_selectedModel->differenceCount() - 1 ) ) + { +// kDebug(8101) << "has next difference in same model" << endl; + return true; + } + + if ( hasNextModel() ) + { +// kDebug(8101) << "has next difference but in next model" << endl; + return true; + } + +// kDebug(8101) << "doesn't have a next difference, not even in next model because there is no next model" << endl; + + return false; +} + +void KompareModelList::slotActionApplyDifference() +{ + if ( !m_selectedDifference->applied() ) + slotApplyDifference( true ); + slotNextDifference(); + updateModelListActions(); +} + +void KompareModelList::slotActionUnApplyDifference() +{ + if ( m_selectedDifference->applied() ) + slotApplyDifference( false ); + slotPreviousDifference(); + updateModelListActions(); +} + +void KompareModelList::slotActionApplyAllDifferences() +{ + slotApplyAllDifferences( true ); + updateModelListActions(); +} + +void KompareModelList::slotActionUnapplyAllDifferences() +{ + slotApplyAllDifferences( false ); + updateModelListActions(); +} + +#include "komparemodellist.moc" + +/* vim: set ts=4 sw=4 noet: */ diff --git a/libkomparediff2/komparemodellist.h b/libkomparediff2/komparemodellist.h new file mode 100644 index 00000000..924097db --- /dev/null +++ b/libkomparediff2/komparemodellist.h @@ -0,0 +1,215 @@ +/*************************************************************************** + komparemodellist.h + ------------------- + begin : Tue Jun 26 2001 + Copyright 2001-2003 John Firebaugh + Copyright 2001-2005,2009 Otto Bruggeman + Copyright 2007-2008 Kevin Kofler + Copyright 2012 Jean-Nicolas Artaud + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the 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 KOMPAREMODELLIST_H +#define KOMPAREMODELLIST_H + +#include + +#include "diffmodel.h" +#include "diffmodellist.h" +#include "kompare.h" +#include "diff2export.h" + + +class KAction; +class KActionCollection; +class KTemporaryFile; + +class DiffSettings; +class KompareProcess; + +namespace Diff2 +{ + +class DIFF2_EXPORT KompareModelList : public QObject +{ + Q_OBJECT +public: + KompareModelList( DiffSettings* diffSettings, QWidget* widgetForKIO, QObject* parent, const char* name = 0, bool isReadWrite = true); + ~KompareModelList(); + +public: + void refresh(); + // Swap source with destination and show differences + void swap(); + + /* Comparing methods */ + bool compare(); + + bool compare(Kompare::Mode); + + bool openDiff( const QString& diff ); + + bool openFileAndDiff(); + bool openDirAndDiff(); + + bool saveDiff( const QString& url, QString directory, DiffSettings* diffSettings ); + bool saveAll(); + + bool saveDestination( DiffModel* model ); + + void setEncoding( const QString& encoding ); + + QString recreateDiff() const; + + // This parses the difflines and creates new models + int parseDiffOutput( const QString& diff ); + + // This open the difflines after parsing them + bool parseAndOpenDiff( const QString& diff ); + + // Call this to emit the signals to the rest of the "world" to show the diff + void show(); + + // This will blend the original URL (dir or file) into the diffmodel, + // this is like patching but with a twist + bool blendOriginalIntoModelList( const QString& localURL ); + + // This mode() method is superfluous now so FIXME + enum Kompare::Mode mode() const { return m_info->mode; }; + const DiffModelList* models() const { return m_models; }; + + KActionCollection* actionCollection() const; + int modelCount() const; + int differenceCount() const; + int appliedCount() const; + + const DiffModel* modelAt( int i ) const { return m_models->at( i ); }; + DiffModel* modelAt( int i ) { return m_models->at( i ); }; + int findModel( DiffModel* model ) const { return m_models->indexOf( model ); }; + + bool hasUnsavedChanges() const; + + int currentModel() const { return m_models->indexOf( m_selectedModel ); }; + int currentDifference() const { return m_selectedModel ? m_selectedModel->findDifference( m_selectedDifference ) : -1; }; + + const DiffModel* selectedModel() const { return m_selectedModel; }; + const Difference* selectedDifference() const { return m_selectedDifference; }; + + void clear(); + +private: + Diff2::DiffModel* firstModel(); + Diff2::DiffModel* lastModel(); + Diff2::DiffModel* prevModel(); + Diff2::DiffModel* nextModel(); + + bool setSelectedModel( Diff2::DiffModel* model ); + + void updateModelListActions(); + +protected: + bool blendFile( DiffModel* model, const QString& lines ); + +signals: + void status( Kompare::Status status ); + void setStatusBarModelInfo( int modelIndex, int differenceIndex, int modelCount, int differenceCount, int appliedCount ); + void error( QString error ); + void modelsChanged( const Diff2::DiffModelList* models ); + void setSelection( const Diff2::DiffModel* model, const Diff2::Difference* diff ); + void setSelection( const Diff2::Difference* diff ); + void applyDifference( bool apply ); + void applyAllDifferences( bool apply ); + void applyDifference( const Diff2::Difference* diff, bool apply ); + void diffString( const QString& ); + void updateActions(); + +public slots: + void slotSelectionChanged( const Diff2::DiffModel* model, const Diff2::Difference* diff ); + void slotSelectionChanged( const Diff2::Difference* diff ); + + void slotApplyDifference( bool apply ); + void slotApplyAllDifferences( bool apply ); + void slotPreviousModel(); + void slotNextModel(); + void slotPreviousDifference(); + void slotNextDifference(); + + void slotKompareInfo( struct Kompare::Info* ); + +protected slots: + void slotDiffProcessFinished( bool success ); + void slotWriteDiffOutput( bool success ); + + void slotActionApplyDifference(); + void slotActionUnApplyDifference(); + void slotActionApplyAllDifferences(); + void slotActionUnapplyAllDifferences(); + + /** Save the currently selected destination in a multi-file diff, + or the single destination if a single file diff. */ + void slotSaveDestination(); + +private slots: + void slotDirectoryChanged( const QString& ); + void slotFileChanged( const QString& ); + +private: // Helper methods + bool isDirectory( const QString& url ) const; + bool isDiff( const QString& mimetype ) const; + QString readFile( const QString& fileName ); + + bool hasPrevModel() const; + bool hasNextModel() const; + bool hasPrevDiff() const; + bool hasNextDiff() const; + + QStringList split( const QString& diff ); + void setDepthAndApplied(); + +private: + KTemporaryFile* m_diffTemp; + QString m_diffURL; + + KompareProcess* m_diffProcess; + + DiffSettings* m_diffSettings; + + DiffModelList* m_models; + + DiffModel* m_selectedModel; + Difference* m_selectedDifference; + + int m_modelIndex; + + struct Kompare::Info* m_info; + + KActionCollection* m_actionCollection; + KAction* m_applyDifference; + KAction* m_unApplyDifference; + KAction* m_applyAll; + KAction* m_unapplyAll; + KAction* m_previousFile; + KAction* m_nextFile; + KAction* m_previousDifference; + KAction* m_nextDifference; + + KAction* m_save; + + QString m_encoding; + QTextCodec* m_textCodec; + + QWidget* m_widgetForKIO; + bool m_isReadWrite; +}; + +} // End of namespace Diff2 + +#endif diff --git a/libkomparediff2/kompareprocess.cpp b/libkomparediff2/kompareprocess.cpp new file mode 100644 index 00000000..230da1c2 --- /dev/null +++ b/libkomparediff2/kompareprocess.cpp @@ -0,0 +1,284 @@ +/*************************************************************************** + kompareprocess.cpp + ------------------ + begin : Sun Mar 4 2001 + Copyright 2001-2005,2009 Otto Bruggeman + Copyright 2001-2003 John Firebaugh + Copyright 2007-2008 Kevin Kofler +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 "kompareprocess.h" + +#include +#include +#include + +#include +#include +#include + +#include "diffsettings.h" + +KompareProcess::KompareProcess( DiffSettings* diffSettings, Kompare::DiffMode diffMode, const QString & source, const QString & destination, const QString &dir, Kompare::Mode mode ) + : KProcess(), + m_diffSettings( diffSettings ), + m_mode( diffMode ), + m_customString(0), + m_textDecoder( 0 ) +{ + // connect the signal that indicates that the proces has exited + connect( this, SIGNAL( finished( int, QProcess::ExitStatus ) ), + SLOT ( slotFinished( int, QProcess::ExitStatus ) ) ); + + setEnv( "LANG", "C" ); + + // Write command and options + if( m_mode == Kompare::Default ) + { + writeDefaultCommandLine(); + } + else + { + writeCommandLine(); + } + + if( !dir.isEmpty() ) { + setWorkingDirectory( dir ); + } + + // Write file names + *this << "--"; + + //Add the option for diff to read from stdin(QIODevice::write), and save a pointer to the string + if(mode == Kompare::ComparingStringFile) + { + *this << "-"; + m_customString = &source; + } + else + { + *this << constructRelativePath( dir, source ); + } + + if(mode == Kompare::ComparingFileString) + { + *this << "-"; + m_customString = &destination; + } + else + { + *this << constructRelativePath( dir, destination ); + } +} + +void KompareProcess::writeDefaultCommandLine() +{ + if ( !m_diffSettings || m_diffSettings->m_diffProgram.isEmpty() ) + { + *this << "diff" << "-dr"; + } + else + { + *this << m_diffSettings->m_diffProgram << "-dr"; + } + + *this << "-U" << QString::number( m_diffSettings->m_linesOfContext ); +} + +void KompareProcess::writeCommandLine() +{ + // load the executable into the KProcess + if ( m_diffSettings->m_diffProgram.isEmpty() ) + { + kDebug(8101) << "Using the first diff in the path..." << endl; + *this << "diff"; + } + else + { + kDebug(8101) << "Using a user specified diff, namely: " << m_diffSettings->m_diffProgram << endl; + *this << m_diffSettings->m_diffProgram; + } + + switch( m_diffSettings->m_format ) { + case Kompare::Unified : + *this << "-U" << QString::number( m_diffSettings->m_linesOfContext ); + break; + case Kompare::Context : + *this << "-C" << QString::number( m_diffSettings->m_linesOfContext ); + break; + case Kompare::RCS : + *this << "-n"; + break; + case Kompare::Ed : + *this << "-e"; + break; + case Kompare::SideBySide: + *this << "-y"; + break; + case Kompare::Normal : + case Kompare::UnknownFormat : + default: + break; + } + + if ( m_diffSettings->m_largeFiles +// default diff does not have -H on OpenBSD +// so don't pass this option unless the user overrode the default program +#if defined(__OpenBSD__) + && !m_diffSettings->m_diffProgram.isEmpty() +#endif + ) + { + *this << "-H"; + } + + if ( m_diffSettings->m_ignoreWhiteSpace ) + { + *this << "-b"; + } + + if ( m_diffSettings->m_ignoreAllWhiteSpace ) + { + *this << "-w"; + } + + if ( m_diffSettings->m_ignoreEmptyLines ) + { + *this << "-B"; + } + + if ( m_diffSettings->m_ignoreChangesDueToTabExpansion ) + { + *this << "-E"; + } + + if ( m_diffSettings->m_createSmallerDiff ) + { + *this << "-d"; + } + + if ( m_diffSettings->m_ignoreChangesInCase ) + { + *this << "-i"; + } + + if ( m_diffSettings->m_ignoreRegExp && !m_diffSettings->m_ignoreRegExpText.isEmpty() ) + { + *this << "-I" << m_diffSettings->m_ignoreRegExpText; + } + + if ( m_diffSettings->m_showCFunctionChange ) + { + *this << "-p"; + } + + if ( m_diffSettings->m_convertTabsToSpaces ) + { + *this << "-t"; + } + + if ( m_diffSettings->m_recursive ) + { + *this << "-r"; + } + + if ( m_diffSettings->m_newFiles ) + { + *this << "-N"; + } + +// This option is more trouble than it is worth... please do not ever enable it unless you want really weird crashes +// if ( m_diffSettings->m_allText ) +// { +// *this << "-a"; +// } + + if ( m_diffSettings->m_excludeFilePattern ) + { + Q_FOREACH(const QString& it, m_diffSettings->m_excludeFilePatternList) + { + *this << "-x" << it; + } + } + + if ( m_diffSettings->m_excludeFilesFile && !m_diffSettings->m_excludeFilesFileURL.isEmpty() ) + { + *this << "-X" << m_diffSettings->m_excludeFilesFileURL; + } +} + +KompareProcess::~KompareProcess() +{ + delete m_textDecoder; +} + +void KompareProcess::setEncoding( const QString& encoding ) +{ + if ( !encoding.compare( "default", Qt::CaseInsensitive ) ) + { + m_textDecoder = QTextCodec::codecForLocale()->makeDecoder(); + } + else + { + m_codec = KGlobal::charsets()->codecForName( encoding.toLatin1() ); + if ( m_codec ) + m_textDecoder = m_codec->makeDecoder(); + else + { + kDebug(8101) << "Using locale codec as backup..." << endl; + m_codec = QTextCodec::codecForLocale(); + m_textDecoder = m_codec->makeDecoder(); + } + } +} + +void KompareProcess::start() +{ +#ifndef NDEBUG + QString cmdLine; + QStringList program = KProcess::program(); + QStringList::ConstIterator it = program.constBegin(); + QStringList::ConstIterator end = program.constEnd(); + for (; it != end; ++it ) + cmdLine += "\"" + (*it) + "\" "; + kDebug(8101) << cmdLine << endl; +#endif + setOutputChannelMode( SeparateChannels ); + setNextOpenMode(QIODevice::ReadWrite); + KProcess::start(); + + //If we have a string to compare against input it now + if(m_customString) + write(m_codec->fromUnicode(*m_customString)); + closeWriteChannel(); +} + +void KompareProcess::slotFinished( int exitCode, QProcess::ExitStatus exitStatus ) +{ + // add all output to m_stdout/m_stderr + if ( m_textDecoder ) + { + m_stdout = m_textDecoder->toUnicode( readAllStandardOutput() ); + m_stderr = m_textDecoder->toUnicode( readAllStandardError() ); + } + else + kDebug(8101) << "KompareProcess::slotFinished : No decoder !!!" << endl; + + // exit code of 0: no differences + // 1: some differences + // 2: error but there may be differences ! + kDebug(8101) << "Exited with exit code : " << exitCode << endl; + emit diffHasFinished( exitStatus == NormalExit && exitCode != 0 ); +} + +#include "kompareprocess.moc" + diff --git a/libkomparediff2/kompareprocess.h b/libkomparediff2/kompareprocess.h new file mode 100644 index 00000000..288d9399 --- /dev/null +++ b/libkomparediff2/kompareprocess.h @@ -0,0 +1,67 @@ +/*************************************************************************** + kompareprocess.h + ---------------- + begin : Sun Mar 4 2001 + Copyright 2001-2003 Otto Bruggeman + Copyright 2001-2003 John Firebaugh + Copyright 2008 Kevin Kofler +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 KOMPAREPROCESS_H +#define KOMPAREPROCESS_H + +#include + +#include "kompare.h" + +class QTextDecoder; + +class DiffSettings; + +class KompareProcess : public KProcess, public KompareFunctions +{ + Q_OBJECT + +public: + KompareProcess( DiffSettings* diffSettings, enum Kompare::DiffMode mode, const QString & source, + const QString & destination, const QString& directory = QString(), enum Kompare::Mode = Kompare::UnknownMode ); + ~KompareProcess(); + + void start(); + + QString diffOutput() { return m_stdout; } + QString stdOut() { return m_stdout; } + QString stdErr() { return m_stderr; } + + void setEncoding( const QString& encoding ); + +signals: + void diffHasFinished( bool finishedNormally ); + +protected: + void writeDefaultCommandLine(); + void writeCommandLine(); + +protected slots: + void slotFinished( int, QProcess::ExitStatus ); + +private: + DiffSettings* m_diffSettings; + enum Kompare::DiffMode m_mode; + const QString * m_customString; // Used when a comparison between a file and a string is requested + QString m_stdout; + QString m_stderr; + QTextDecoder* m_textDecoder; + QTextCodec * m_codec; +}; + +#endif diff --git a/libkomparediff2/levenshteintable.h b/libkomparediff2/levenshteintable.h new file mode 100644 index 00000000..77d806f1 --- /dev/null +++ b/libkomparediff2/levenshteintable.h @@ -0,0 +1,403 @@ +/******************************************************************************* +** +** Filename : levenshteintable.h +** Created on : 08 november, 2003 +** Copyright 2003 Otto Bruggeman +** Copyright 2011 Dmitry Risenberg +** +*******************************************************************************/ + +/******************************************************************************* +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 LEVENSHTEIN_H +#define LEVENSHTEIN_H + +#include +#include + +#include + +namespace Diff2 { + +class Marker; + + +class Marker; + +/** + * Computes the Levenshtein distance between two sequences. + * The actual sequence contents must be prepended with one virtual item each for easier index access. + */ +template class LevenshteinTable +{ +public: + LevenshteinTable(); + LevenshteinTable( unsigned int width, unsigned int height ); + ~LevenshteinTable(); + +public: + int getContent( unsigned int posX, unsigned int posY ) const; + int setContent( unsigned int posX, unsigned int posY, int value ); + bool setSize ( unsigned int width, unsigned int height ); + + unsigned int width() const { return m_width; }; + unsigned int height() const { return m_height; }; + + /** Debug method to check if the table is properly filled */ + void dumpLevenshteinTable( void ); + + /** + * This calculates the levenshtein distance of 2 sequences. + * This object takes ownership of the argument + */ + unsigned int createTable( SequencePair* sequences ); + + void createListsOfMarkers( void ); + int chooseRoute( int c1, int c2, int c3, int current ); + +protected: + LevenshteinTable( const LevenshteinTable& table ); + const LevenshteinTable& operator = ( const LevenshteinTable& table ); + +private: + unsigned int m_width; + unsigned int m_height; + unsigned int m_size; + unsigned int* m_table; + SequencePair* m_sequences; +}; + +template LevenshteinTable::LevenshteinTable() +: m_width( 256 ), +m_height( 256 ), +m_size( m_height * m_width ), +m_table( new unsigned int[ m_size ] ), +m_sequences(0) +{ +} + +template LevenshteinTable::LevenshteinTable( unsigned int width, unsigned int height ) +: m_width( width ), +m_height( height ), +m_size( m_width * m_height ), +m_table( new unsigned int[ m_size ] ), +m_sequences(0) +{ +} + +template LevenshteinTable::~LevenshteinTable() +{ + delete[] m_table; + delete m_sequences; +} + +template int LevenshteinTable::getContent( unsigned int posX, unsigned int posY ) const +{ +// kDebug(8101) << "Width = " << m_width << ", height = " << m_height << ", posX = " << posX << ", posY = " << posY; + return m_table[ posY * m_width + posX ]; +} + +template int LevenshteinTable::setContent( unsigned int posX, unsigned int posY, int value ) +{ + m_table[ posY * m_width + posX ] = value; + + return 0; +} + +template bool LevenshteinTable::setSize( unsigned int width, unsigned int height ) +{ +// Set a limit of 16.7 million entries, will be about 64 MB of ram, that should be plenty + if ( ( ( width ) * ( height ) ) > ( 256 * 256 * 256 ) ) + return false; + + if ( ( ( width ) * ( height ) ) > m_size ) + { + delete[] m_table; + + m_size = width * height; + m_table = new unsigned int[ m_size ]; + } + + m_width = width; + m_height = height; + + return true; +} + +template void LevenshteinTable::dumpLevenshteinTable() +{ + for ( unsigned int i = 0; i < m_height; ++i ) + { + for ( unsigned int j = 0; j < m_width; ++j ) + { + std::cout.width( 3 ); + std::cout << getContent( j, i ); + } + std::cout << std::endl; + } +} + +template unsigned int LevenshteinTable::createTable( SequencePair* sequences ) +{ + m_sequences = sequences; + unsigned int m = m_sequences->lengthFirst(); + unsigned int n = m_sequences->lengthSecond(); + + if ( !setSize( m, n ) ) + return 0; + + unsigned int i; + unsigned int j; + +// initialize first row + for ( i = 0; i < m; ++i ) + setContent( i, 0, i ); +// initialize first column + for ( j = 0; j < n; ++j ) + setContent( 0, j, j ); + + int cost = 0, north = 0, west = 0, northwest = 0; + + QChar si, dj; +// Optimization, calculate row wise instead of column wise, wont trash the cache so much with large strings + for ( j = 1; j < n; ++j ) + { + for ( i = 1; i < m; ++i ) + { + if (m_sequences->equal(i, j)) + cost = 0; + else + cost = SequencePair::allowReplace? 1 : 2; + + north = getContent( i, j-1 ) + 1; + west = getContent( i-1, j ) + 1; + northwest = getContent( i-1, j-1 ) + cost; + + setContent( i, j, qMin( north, qMin( west, northwest ) ) ); + } + } + + return getContent( m-1, n-1 ); +} + +template int LevenshteinTable::chooseRoute( int c1, int c2, int c3, int current ) +{ +// kDebug(8101) << "c1 = " << c1 << ", c2 = " << c2 << ", c3 = " << c3; +// preference order: c2, c3, c1, hopefully this will work out for me + if ( c2 <= c1 && c2 <= c3 ) + { + if ( SequencePair::allowReplace || (c2 == current) ) + { + return 1; + } + } + + if ( c3 <= c2 && c3 <= c1 ) + return 2; + + return 0; +} + +template void LevenshteinTable::createListsOfMarkers() +{ +// kDebug(8101) << source; +// kDebug(8101) << destination; +// dumpLevenshteinTable(); + + unsigned int x = m_width-1; + unsigned int y = m_height-1; + + unsigned int difference = getContent(x, y); + +// If the number of differences is more than half the length of the largest string +// dont bother to mark the individual changes +// Patch based on work by Felix Berger as put as attachment to bug 75794 + if ( !m_sequences->needFineGrainedOutput(difference) ) + { + m_sequences->prependFirst( new Marker( Marker::End, x ) ); + m_sequences->prependFirst( new Marker( Marker::Start, 0 ) ); + m_sequences->prependSecond( new Marker( Marker::End, y ) ); + m_sequences->prependSecond( new Marker( Marker::Start, 0 ) ); + return; + } + + Marker* c = 0; + + int n, nw, w, direction, currentValue; + while ( x > 0 && y > 0 ) + { + currentValue = getContent( x, y ); + + n = getContent( x, y - 1 ); + w = getContent( x - 1, y ); + nw = getContent( x - 1, y - 1 ); + direction = chooseRoute( n, nw, w, currentValue ); + + switch ( direction ) + { + case 0: // north +// kDebug(8101) << "Picking north"; +// kDebug(8101) << "Source[" << ( x - 1 ) << "] = " << QString( source[ x-1 ] ) << ", destination[" << ( y - 1 ) << "] = " << QString( destination[ y-1 ] ); + + if ( !m_sequences->markerListSecond().isEmpty() ) + c = m_sequences->markerListSecond().first(); + else + c = 0; + + if ( c && c->type() == Marker::End ) + { +// kDebug(8101) << "CurrentValue: " << currentValue; + if ( n == currentValue ) + m_sequences->prependSecond( new Marker( Marker::Start, y ) ); +// else: the change continues, do not do anything + } + else + { +// kDebug(8101) << "CurrentValue: " << currentValue; + if ( n < currentValue ) + m_sequences->prependSecond( new Marker( Marker::End, y ) ); + } + + --y; + if ( y == 0 ) { + m_sequences->prependSecond( new Marker( Marker::Start, 0 ) ); + } + break; + case 1: // northwest +// kDebug(8101) << "Picking northwest"; +// kDebug(8101) << "Source[" << ( x - 1 ) << "] = " << QString( source[ x-1 ] ) << ", destination[" << ( y - 1 ) << "] = " << QString( destination[ y-1 ] ); + + if ( !m_sequences->markerListSecond().isEmpty() ) + c = m_sequences->markerListSecond().first(); + else + c = 0; + + if ( c && c->type() == Marker::End ) + { +// kDebug(8101) << "End found: CurrentValue: " << currentValue; + if ( nw == currentValue ) + m_sequences->prependSecond( new Marker( Marker::Start, y ) ); +// else: the change continues, do not do anything + } + else + { +// kDebug(8101) << "CurrentValue: " << currentValue; + if ( nw < currentValue ) + m_sequences->prependSecond( new Marker( Marker::End, y ) ); + } + + if ( !m_sequences->markerListFirst().isEmpty() ) + c = m_sequences->markerListFirst().first(); + else + c = 0; + + if ( c && c->type() == Marker::End ) + { +// kDebug(8101) << "End found: CurrentValue: " << currentValue; + if ( nw == currentValue ) + m_sequences->prependFirst( new Marker( Marker::Start, x ) ); +// else: the change continues, do not do anything + } + else + { +// kDebug(8101) << "CurrentValue: " << currentValue; + if ( nw < currentValue ) + m_sequences->prependFirst( new Marker( Marker::End, x ) ); + } + + --y; + --x; + break; + case 2: // west +// kDebug(8101) << "Picking west"; +// kDebug(8101) << "Source[" << ( x - 1 ) << "] = " << QString( source[ x-1 ] ) << ", destination[" << ( y - 1 ) << "] = " << QString( destination[ y-1 ] ); + + if ( !m_sequences->markerListFirst().isEmpty() ) + c = m_sequences->markerListFirst().first(); + else + c = 0; + + if ( c && c->type() == Marker::End ) + { +// kDebug(8101) << "End found: CurrentValue: " << currentValue; + if ( w == currentValue ) + m_sequences->prependFirst( new Marker( Marker::Start, x ) ); +// else: the change continues, do not do anything + } + else + { +// kDebug(8101) << "CurrentValue: " << currentValue; + if ( w < currentValue ) + m_sequences->prependFirst( new Marker( Marker::End, x ) ); + } + + --x; + if (x == 0) { + m_sequences->prependFirst( new Marker( Marker::Start, 0 ) ); + } + break; + } + } + +// When leaving the loop it does not mean both are 0! If not there is still a change at the beginning of the line we missed so adding now. + if ( x != 0 ) + { + m_sequences->prependFirst( new Marker( Marker::End, x ) ); + m_sequences->prependFirst( new Marker( Marker::Start, 0 ) ); + } + + if ( y != 0 ) + { + m_sequences->prependSecond( new Marker( Marker::End, y ) ); + m_sequences->prependSecond( new Marker( Marker::Start, 0 ) ); + } + +// kDebug(8101) << "Source string: " << source; + +// QStringList list; +// int prevValue = 0; +// MarkerListConstIterator mit = m_sequences->markerListFirst().begin(); +// MarkerListConstIterator end = m_sequences->markerListFirst().end(); +// for ( ; mit != end; ++mit ) +// { +// c = *mit; +// kDebug(8101) << "Source Marker Entry : Type: " << c->type() << ", Offset: " << c->offset(); +// list.append( source.mid( prevValue, c->offset() - prevValue ) ); +// prevValue = c->offset(); +// } +// if ( prevValue < source.length() - 1 ) +// { +// list.append( source.mid( prevValue, source.length() - prevValue ) ); +// } +// kDebug(8101) << "Source Resulting stringlist : " << list.join("\n"); +// +// list.clear(); +// prevValue = 0; +// +// kDebug(8101) << "Destination string: " << destination; +// mit = m_sequences->markerListSecond().begin(); +// end = m_sequences->markerListSecond().end(); +// for ( ; mit != end; ++mit ) +// { +// c = *mit; +// kDebug(8101) << "Destination Marker Entry : Type: " << c->type() << ", Offset: " << c->offset(); +// list.append( destination.mid( prevValue, c->offset() - prevValue ) ); +// prevValue = c->offset(); +// } +// if ( prevValue < destination.length() - 1 ) +// { +// list.append( destination.mid( prevValue, destination.length() - prevValue ) ); +// } +// kDebug(8101) << "Destination Resulting string : " << list.join("\n"); +} + +} // namespace Diff2 + +#endif // LEVENSHTEIN_H diff --git a/libkomparediff2/marker.h b/libkomparediff2/marker.h new file mode 100644 index 00000000..a8b5661b --- /dev/null +++ b/libkomparediff2/marker.h @@ -0,0 +1,69 @@ +/* + * This file is part of KDevelop + * Copyright 2011 Dmitry Risenberg + * + * 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 General Public + * 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 MARKER_H +#define MARKER_H + +#include +#include "diff2export.h" + +namespace Diff2 { + +class DIFF2_EXPORT Marker +{ +public: + enum Type { Start = 0, End = 1 }; + +public: + Marker() + { + m_type = Marker::Start; + m_offset = 0; + } + Marker( enum Marker::Type type, unsigned int offset ) + { + m_type = type; + m_offset = offset; + } + ~Marker() {} + +public: + enum Marker::Type type() const { return m_type; } + unsigned int offset() const { return m_offset; } + + void setType ( enum Marker::Type type ) { m_type = type; } + void setOffset( unsigned int offset ) { m_offset = offset; } + + bool operator == (const Marker& rhs) const { + return this->type() == rhs.type() && this->offset() == rhs.offset(); + } + +private: + enum Marker::Type m_type; + unsigned int m_offset; +}; + +typedef QList MarkerList; +typedef QList::iterator MarkerListIterator; +typedef QList::const_iterator MarkerListConstIterator; + +} + +#endif // MARKER_H diff --git a/libkomparediff2/parser.cpp b/libkomparediff2/parser.cpp new file mode 100644 index 00000000..e4e67775 --- /dev/null +++ b/libkomparediff2/parser.cpp @@ -0,0 +1,139 @@ +/************************************************************************** +** parser.cpp +** ---------- +** begin : Sun Aug 4 15:05:35 2002 +** Copyright 2002-2004 Otto Bruggeman +** Copyright 2010 Kevin Kofler +***************************************************************************/ +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 "parser.h" + +#include + +#include "cvsdiffparser.h" +#include "diffparser.h" +#include "perforceparser.h" +#include "diffmodel.h" + +using namespace Diff2; + +Parser::Parser( const KompareModelList* list ) : + m_list( list ) +{ +} + +Parser::~Parser() +{ +} + +int Parser::cleanUpCrap( QStringList& diffLines ) +{ + QStringList::Iterator it = diffLines.begin(); + + int nol = 0; + + QString noNewLine( "\\ No newline" ); + + for ( ; it != diffLines.end(); ++it ) + { + if ( (*it).startsWith( noNewLine ) ) + { + it = diffLines.erase( it ); + // correcting the advance of the iterator because of the remove + --it; + QString temp( *it ); + temp.truncate( temp.indexOf( '\n' ) ); + *it = temp; + ++nol; + } + } + + return nol; +} + +DiffModelList* Parser::parse( QStringList& diffLines, bool* malformed ) +{ + /* Basically determine the generator then call the parse method */ + ParserBase* parser; + + m_generator = determineGenerator( diffLines ); + + int nol = cleanUpCrap( diffLines ); + kDebug(8101) << "Cleaned up " << nol << " line(s) of crap from the diff..." << endl; + + switch( m_generator ) + { + case Kompare::CVSDiff : + kDebug(8101) << "It is a CVS generated diff..." << endl; + parser = new CVSDiffParser( m_list, diffLines ); + break; + case Kompare::Diff : + kDebug(8101) << "It is a diff generated diff..." << endl; + parser = new DiffParser( m_list, diffLines ); + break; + case Kompare::Perforce : + kDebug(8101) << "It is a Perforce generated diff..." << endl; + parser = new PerforceParser( m_list, diffLines ); + break; + default: + // Nothing to delete, just leave... + return 0L; + } + + m_format = parser->format(); + DiffModelList* modelList = parser->parse( malformed ); + if ( modelList ) + { + kDebug(8101) << "Modelcount: " << modelList->count() << endl; + DiffModelListIterator modelIt = modelList->begin(); + DiffModelListIterator mEnd = modelList->end(); + for ( ; modelIt != mEnd; ++modelIt ) + { + kDebug(8101) << "Hunkcount: " << (*modelIt)->hunkCount() << endl; + kDebug(8101) << "Diffcount: " << (*modelIt)->differenceCount() << endl; + } + } + + delete parser; + + return modelList; +} + +enum Kompare::Generator Parser::determineGenerator( const QStringList& diffLines ) +{ + // Shit have to duplicate some code with this method and the ParserBase derived classes + QString cvsDiff ( "Index: " ); + QString perforceDiff( "==== " ); + + QStringList::ConstIterator it = diffLines.begin(); + QStringList::ConstIterator linesEnd = diffLines.end(); + + while ( it != linesEnd ) + { + if ( ( *it ).startsWith( cvsDiff ) ) + { + kDebug(8101) << "Diff is a CVSDiff" << endl; + return Kompare::CVSDiff; + } + else if ( ( *it ).startsWith( perforceDiff ) ) + { + kDebug(8101) << "Diff is a Perforce Diff" << endl; + return Kompare::Perforce; + } + ++it; + } + + kDebug(8101) << "We'll assume it is a diff Diff" << endl; + + // For now we'll assume it is a diff file diff, later we might + // try to really determine if it is a diff file diff. + return Kompare::Diff; +} diff --git a/libkomparediff2/parser.h b/libkomparediff2/parser.h new file mode 100644 index 00000000..984345d6 --- /dev/null +++ b/libkomparediff2/parser.h @@ -0,0 +1,57 @@ +/************************************************************************** +** parser.h +** -------- +** begin : Tue Jul 30 23:53:52 2002 +** Copyright 2002-2004 Otto Bruggeman +** Copyright 2010 Kevin Kofler +***************************************************************************/ +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 DIFF2_PARSER_H +#define DIFF2_PARSER_H + +#include "diffmodellist.h" +#include "kompare.h" + +namespace Diff2 +{ + +class DiffModel; +class KompareModelList; + +class Parser +{ +public: + Parser( const KompareModelList* list ); + ~Parser(); + +public: + DiffModelList* parse( QStringList& diffLines, bool* malformed = 0 ); + + enum Kompare::Generator generator() const { return m_generator; }; + enum Kompare::Format format() const { return m_format; }; + +private: + /** Which program was used to generate the output */ + enum Kompare::Generator determineGenerator( const QStringList& diffLines ); + + int cleanUpCrap( QStringList& diffLines ); + +private: + enum Kompare::Generator m_generator; + enum Kompare::Format m_format; + + const KompareModelList* m_list; +}; + +} // End of namespace Diff2 + +#endif + diff --git a/libkomparediff2/parserbase.cpp b/libkomparediff2/parserbase.cpp new file mode 100644 index 00000000..53325bca --- /dev/null +++ b/libkomparediff2/parserbase.cpp @@ -0,0 +1,774 @@ +/************************************************************************** +** parserbase.cpp +** -------------- +** begin : Sun Aug 4 15:05:35 2002 +** Copyright 2002-2004,2009 Otto Bruggeman +** Copyright 2007,2010 Kevin Kofler +***************************************************************************/ +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 "parserbase.h" + +#include + +#include + +#include "diffmodel.h" +#include "diffhunk.h" +#include "difference.h" +#include "komparemodellist.h" + +using namespace Diff2; + +ParserBase::ParserBase( const KompareModelList* list, const QStringList& diff ) : + m_diffLines( diff ), + m_currentModel( 0 ), + m_models( 0 ), + m_diffIterator( m_diffLines.begin() ), + m_singleFileDiff( false ), + m_malformed( false ), + m_list( list ) +{ +// kDebug(8101) << diff << endl; +// kDebug(8101) << m_diffLines << endl; + m_models = new DiffModelList(); + + // used in contexthunkheader + m_contextHunkHeader1.setPattern( "\\*{15} ?(.*)\\n" ); // capture is for function name + m_contextHunkHeader2.setPattern( "\\*\\*\\* ([0-9]+),([0-9]+) \\*\\*\\*\\*.*\\n" ); + // used in contexthunkbody + m_contextHunkHeader3.setPattern( "--- ([0-9]+),([0-9]+) ----\\n" ); + + m_contextHunkBodyRemoved.setPattern( "- (.*)" ); + m_contextHunkBodyAdded.setPattern ( "\\+ (.*)" ); + m_contextHunkBodyChanged.setPattern( "! (.*)" ); + m_contextHunkBodyContext.setPattern( " (.*)" ); + m_contextHunkBodyLine.setPattern ( "[-\\+! ] (.*)" ); + + // This regexp sucks... i'll see what happens + m_normalDiffHeader.setPattern( "diff (?:(?:-|--)[a-zA-Z0-9=\\\"]+ )*(?:|-- +)(.*) +(.*)\\n" ); + + m_normalHunkHeaderAdded.setPattern ( "([0-9]+)a([0-9]+)(|,[0-9]+)(.*)\\n" ); + m_normalHunkHeaderRemoved.setPattern( "([0-9]+)(|,[0-9]+)d([0-9]+)(.*)\\n" ); + m_normalHunkHeaderChanged.setPattern( "([0-9]+)(|,[0-9]+)c([0-9]+)(|,[0-9]+)(.*)\\n" ); + + m_normalHunkBodyRemoved.setPattern ( "< (.*)" ); + m_normalHunkBodyAdded.setPattern ( "> (.*)" ); + m_normalHunkBodyDivider.setPattern ( "---" ); + + m_unifiedDiffHeader1.setPattern ( "--- ([^\\t]+)(?:\\t([^\\t]+)(?:\\t?)(.*))?\\n" ); + m_unifiedDiffHeader2.setPattern ( "\\+\\+\\+ ([^\\t]+)(?:\\t([^\\t]+)(?:\\t?)(.*))?\\n" ); + m_unifiedHunkHeader.setPattern ( "@@ -([0-9]+)(|,([0-9]+)) \\+([0-9]+)(|,([0-9]+)) @@(?: ?)(.*)\\n" ); + m_unifiedHunkBodyAdded.setPattern ( "\\+(.*)" ); + m_unifiedHunkBodyRemoved.setPattern( "-(.*)" ); + m_unifiedHunkBodyContext.setPattern( " (.*)" ); + m_unifiedHunkBodyLine.setPattern ( "([-+ ])(.*)" ); +} + +ParserBase::~ParserBase() +{ + if ( m_models ) + m_models = 0; // do not delete this, i pass it around... +} + +enum Kompare::Format ParserBase::determineFormat() +{ + // Write your own format detection routine damn it :) + return Kompare::UnknownFormat; +} + +DiffModelList* ParserBase::parse( bool* malformed ) +{ + DiffModelList* result; + switch( determineFormat() ) + { + case Kompare::Context : + result = parseContext(); + break; + case Kompare::Ed : + result = parseEd(); + break; + case Kompare::Normal : + result = parseNormal(); + break; + case Kompare::RCS : + result = parseRCS(); + break; + case Kompare::Unified : + result = parseUnified(); + break; + default: // Unknown and SideBySide for now + result = 0; + break; + } + + // *malformed is set to true if some hunks or parts of hunks were + // probably missed due to a malformed diff + if ( malformed ) + *malformed = m_malformed; + + return result; +} + +bool ParserBase::parseContextDiffHeader() +{ +// kDebug(8101) << "ParserBase::parseContextDiffHeader()" << endl; + bool result = false; + + while ( m_diffIterator != m_diffLines.end() ) + { + if ( !m_contextDiffHeader1.exactMatch( *(m_diffIterator)++ ) ) + { + continue; + } +// kDebug(8101) << "Matched length Header1 = " << m_contextDiffHeader1.matchedLength() << endl; +// kDebug(8101) << "Matched string Header1 = " << m_contextDiffHeader1.cap( 0 ) << endl; + if ( m_diffIterator != m_diffLines.end() && m_contextDiffHeader2.exactMatch( *m_diffIterator ) ) + { +// kDebug(8101) << "Matched length Header2 = " << m_contextDiffHeader2.matchedLength() << endl; +// kDebug(8101) << "Matched string Header2 = " << m_contextDiffHeader2.cap( 0 ) << endl; + + m_currentModel = new DiffModel( m_contextDiffHeader1.cap( 1 ), m_contextDiffHeader2.cap( 1 ) ); + m_currentModel->setSourceTimestamp ( m_contextDiffHeader1.cap( 3 ) ); + m_currentModel->setSourceRevision ( m_contextDiffHeader1.cap( 5 ) ); + m_currentModel->setDestinationTimestamp( m_contextDiffHeader2.cap( 3 ) ); + m_currentModel->setDestinationRevision ( m_contextDiffHeader2.cap( 5 ) ); + + ++m_diffIterator; + result = true; + + break; + } + else + { + // We're screwed, second line does not match or is not there... + break; + } + // Do not inc the Iterator because the second line might be the first line of + // the context header and the first hit was a fluke (impossible imo) + // maybe we should return false here because the diff is broken ? + } + + return result; +} + +bool ParserBase::parseEdDiffHeader() +{ + return false; +} + +bool ParserBase::parseNormalDiffHeader() +{ +// kDebug(8101) << "ParserBase::parseNormalDiffHeader()" << endl; + bool result = false; + + while ( m_diffIterator != m_diffLines.end() ) + { + if ( m_normalDiffHeader.exactMatch( *m_diffIterator ) ) + { +// kDebug(8101) << "Matched length Header = " << m_normalDiffHeader.matchedLength() << endl; +// kDebug(8101) << "Matched string Header = " << m_normalDiffHeader.cap( 0 ) << endl; + + m_currentModel = new DiffModel(); + m_currentModel->setSourceFile ( m_normalDiffHeader.cap( 1 ) ); + m_currentModel->setDestinationFile ( m_normalDiffHeader.cap( 2 ) ); + + result = true; + + ++m_diffIterator; + break; + } + else + { + kDebug(8101) << "No match for: " << ( *m_diffIterator ) << endl; + } + ++m_diffIterator; + } + + if ( result == false ) + { + // Set this to the first line again and hope it is a single file diff + m_diffIterator = m_diffLines.begin(); + m_currentModel = new DiffModel(); + m_singleFileDiff = true; + } + + return result; +} + +bool ParserBase::parseRCSDiffHeader() +{ + return false; +} + +bool ParserBase::parseUnifiedDiffHeader() +{ +// kDebug(8101) << "ParserBase::parseUnifiedDiffHeader()" << endl; + bool result = false; + + while ( m_diffIterator != m_diffLines.end() ) // do not assume we start with the diffheader1 line + { + if ( !m_unifiedDiffHeader1.exactMatch( *m_diffIterator ) ) + { + ++m_diffIterator; + continue; + } +// kDebug(8101) << "Matched length Header1 = " << m_unifiedDiffHeader1.matchedLength() << endl; +// kDebug(8101) << "Matched string Header1 = " << m_unifiedDiffHeader1.cap( 0 ) << endl; + ++m_diffIterator; + if ( m_diffIterator != m_diffLines.end() && m_unifiedDiffHeader2.exactMatch( *m_diffIterator ) ) + { + m_currentModel = new DiffModel( m_unifiedDiffHeader1.cap( 1 ), m_unifiedDiffHeader2.cap( 1 ) ); + m_currentModel->setSourceTimestamp( m_unifiedDiffHeader1.cap( 2 ) ); + m_currentModel->setSourceRevision( m_unifiedDiffHeader1.cap( 4 ) ); + m_currentModel->setDestinationTimestamp( m_unifiedDiffHeader2.cap( 2 ) ); + m_currentModel->setDestinationRevision( m_unifiedDiffHeader2.cap( 4 ) ); + + ++m_diffIterator; + result = true; + + break; + } + else + { + // We're screwed, second line does not match or is not there... + break; + } + } + + return result; +} + +bool ParserBase::parseContextHunkHeader() +{ +// kDebug(8101) << "ParserBase::parseContextHunkHeader()" << endl; + + if ( m_diffIterator == m_diffLines.end() ) + return false; + + if ( !m_contextHunkHeader1.exactMatch( *(m_diffIterator) ) ) + return false; // big fat trouble, aborting... + + ++m_diffIterator; + + if ( m_diffIterator == m_diffLines.end() ) + return false; + + if ( !m_contextHunkHeader2.exactMatch( *(m_diffIterator) ) ) + return false; // big fat trouble, aborting... + + ++m_diffIterator; + + return true; +} + +bool ParserBase::parseEdHunkHeader() +{ + return false; +} + +bool ParserBase::parseNormalHunkHeader() +{ +// kDebug(8101) << "ParserBase::parseNormalHunkHeader()" << endl; + if ( m_diffIterator != m_diffLines.end() ) + { +// kDebug(8101) << "Header = " << *m_diffIterator << endl; + if ( m_normalHunkHeaderAdded.exactMatch( *m_diffIterator ) ) + { + m_normalDiffType = Difference::Insert; + } + else if ( m_normalHunkHeaderRemoved.exactMatch( *m_diffIterator ) ) + { + m_normalDiffType = Difference::Delete; + } + else if ( m_normalHunkHeaderChanged.exactMatch( *m_diffIterator ) ) + { + m_normalDiffType = Difference::Change; + } + else + return false; + + ++m_diffIterator; + return true; + } + + return false; +} + +bool ParserBase::parseRCSHunkHeader() +{ + return false; +} + +bool ParserBase::parseUnifiedHunkHeader() +{ +// kDebug(8101) << "ParserBase::parseUnifiedHunkHeader()" << endl; + + if ( m_diffIterator != m_diffLines.end() && m_unifiedHunkHeader.exactMatch( *m_diffIterator ) ) + { + ++m_diffIterator; + return true; + } + else + { +// kDebug(8101) << "This is not a unified hunk header : " << (*m_diffIterator) << endl; + return false; + } + +} + +bool ParserBase::parseContextHunkBody() +{ +// kDebug(8101) << "ParserBase::parseContextHunkBody()" << endl; + + // Storing the src part of the hunk for later use + QStringList oldLines; + for( ; m_diffIterator != m_diffLines.end() && m_contextHunkBodyLine.exactMatch( *m_diffIterator ); ++m_diffIterator ) { +// kDebug(8101) << "Added old line: " << *m_diffIterator << endl; + oldLines.append( *m_diffIterator ); + } + + if( !m_contextHunkHeader3.exactMatch( *m_diffIterator ) ) + return false; + + ++m_diffIterator; + + // Storing the dest part of the hunk for later use + QStringList newLines; + for( ; m_diffIterator != m_diffLines.end() && m_contextHunkBodyLine.exactMatch( *m_diffIterator ); ++m_diffIterator ) { +// kDebug(8101) << "Added new line: " << *m_diffIterator << endl; + newLines.append( *m_diffIterator ); + } + + QString function = m_contextHunkHeader1.cap( 1 ); +// kDebug(8101) << "Captured function: " << function << endl; + int linenoA = m_contextHunkHeader2.cap( 1 ).toInt(); +// kDebug(8101) << "Source line number: " << linenoA << endl; + int linenoB = m_contextHunkHeader3.cap( 1 ).toInt(); +// kDebug(8101) << "Dest line number: " << linenoB << endl; + + DiffHunk* hunk = new DiffHunk( linenoA, linenoB, function ); + + m_currentModel->addHunk( hunk ); + + QStringList::Iterator oldIt = oldLines.begin(); + QStringList::Iterator newIt = newLines.begin(); + + Difference* diff; + while( oldIt != oldLines.end() || newIt != newLines.end() ) + { + if( oldIt != oldLines.end() && m_contextHunkBodyRemoved.exactMatch( *oldIt ) ) + { +// kDebug(8101) << "Delete: " << endl; + diff = new Difference( linenoA, linenoB ); + diff->setType( Difference::Delete ); + m_currentModel->addDiff( diff ); +// kDebug(8101) << "Difference added" << endl; + hunk->add( diff ); + for( ; oldIt != oldLines.end() && m_contextHunkBodyRemoved.exactMatch( *oldIt ); ++oldIt ) + { +// kDebug(8101) << " " << m_contextHunkBodyRemoved.cap( 1 ) << endl; + diff->addSourceLine( m_contextHunkBodyRemoved.cap( 1 ) ); + linenoA++; + } + } + else if( newIt != newLines.end() && m_contextHunkBodyAdded.exactMatch( *newIt ) ) + { +// kDebug(8101) << "Insert: " << endl; + diff = new Difference( linenoA, linenoB ); + diff->setType( Difference::Insert ); + m_currentModel->addDiff( diff ); +// kDebug(8101) << "Difference added" << endl; + hunk->add( diff ); + for( ; newIt != newLines.end() && m_contextHunkBodyAdded.exactMatch( *newIt ); ++newIt ) + { +// kDebug(8101) << " " << m_contextHunkBodyAdded.cap( 1 ) << endl; + diff->addDestinationLine( m_contextHunkBodyAdded.cap( 1 ) ); + linenoB++; + } + } + else if( ( oldIt == oldLines.end() || m_contextHunkBodyContext.exactMatch( *oldIt ) ) && + ( newIt == newLines.end() || m_contextHunkBodyContext.exactMatch( *newIt ) ) ) + { +// kDebug(8101) << "Unchanged: " << endl; + diff = new Difference( linenoA, linenoB ); + // Do not add this diff with addDiff to the model... no unchanged differences allowed in there... + diff->setType( Difference::Unchanged ); + hunk->add( diff ); + while( ( oldIt == oldLines.end() || m_contextHunkBodyContext.exactMatch( *oldIt ) ) && + ( newIt == newLines.end() || m_contextHunkBodyContext.exactMatch( *newIt ) ) && + ( oldIt != oldLines.end() || newIt != newLines.end() ) ) + { + QString l; + if( oldIt != oldLines.end() ) + { + l = m_contextHunkBodyContext.cap( 1 ); +// kDebug(8101) << "old: " << l << endl; + ++oldIt; + } + if( newIt != newLines.end() ) + { + l = m_contextHunkBodyContext.cap( 1 ); +// kDebug(8101) << "new: " << l << endl; + ++newIt; + } + diff->addSourceLine( l ); + diff->addDestinationLine( l ); + linenoA++; + linenoB++; + } + } + else if( ( oldIt != oldLines.end() && m_contextHunkBodyChanged.exactMatch( *oldIt ) ) || + ( newIt != newLines.end() && m_contextHunkBodyChanged.exactMatch( *newIt ) ) ) + { +// kDebug(8101) << "Changed: " << endl; + diff = new Difference( linenoA, linenoB ); + diff->setType( Difference::Change ); + m_currentModel->addDiff( diff ); +// kDebug(8101) << "Difference added" << endl; + hunk->add( diff ); + while( oldIt != oldLines.end() && m_contextHunkBodyChanged.exactMatch( *oldIt ) ) + { +// kDebug(8101) << " " << m_contextHunkBodyChanged.cap( 1 ) << endl; + diff->addSourceLine( m_contextHunkBodyChanged.cap( 1 ) ); + linenoA++; + ++oldIt; + } + while( newIt != newLines.end() && m_contextHunkBodyChanged.exactMatch( *newIt ) ) + { +// kDebug(8101) << " " << m_contextHunkBodyChanged.cap( 1 ) << endl; + diff->addDestinationLine( m_contextHunkBodyChanged.cap( 1 ) ); + linenoB++; + ++newIt; + } + } + else + return false; + diff->determineInlineDifferences(); + } + + return true; +} + +bool ParserBase::parseEdHunkBody() +{ + return false; +} + +bool ParserBase::parseNormalHunkBody() +{ +// kDebug(8101) << "ParserBase::parseNormalHunkBody" << endl; + + QString type; + + int linenoA = 0, linenoB = 0; + + if ( m_normalDiffType == Difference::Insert ) + { + linenoA = m_normalHunkHeaderAdded.cap( 1 ).toInt(); + linenoB = m_normalHunkHeaderAdded.cap( 2 ).toInt(); + } + else if ( m_normalDiffType == Difference::Delete ) + { + linenoA = m_normalHunkHeaderRemoved.cap( 1 ).toInt(); + linenoB = m_normalHunkHeaderRemoved.cap( 3 ).toInt(); + } + else if ( m_normalDiffType == Difference::Change ) + { + linenoA = m_normalHunkHeaderChanged.cap( 1 ).toInt(); + linenoB = m_normalHunkHeaderChanged.cap( 3 ).toInt(); + } + + DiffHunk* hunk = new DiffHunk( linenoA, linenoB ); + m_currentModel->addHunk( hunk ); + Difference* diff = new Difference( linenoA, linenoB ); + hunk->add( diff ); + m_currentModel->addDiff( diff ); + + diff->setType( m_normalDiffType ); + + if ( m_normalDiffType == Difference::Change || m_normalDiffType == Difference::Delete ) + for( ; m_diffIterator != m_diffLines.end() && m_normalHunkBodyRemoved.exactMatch( *m_diffIterator ); ++m_diffIterator ) + { +// kDebug(8101) << "Line = " << *m_diffIterator << endl; + diff->addSourceLine( m_normalHunkBodyRemoved.cap( 1 ) ); + } + + if ( m_normalDiffType == Difference::Change ) + { + if( m_diffIterator != m_diffLines.end() && m_normalHunkBodyDivider.exactMatch( *m_diffIterator ) ) + { +// kDebug(8101) << "Line = " << *m_diffIterator << endl; + ++m_diffIterator; + } + else + return false; + } + + if ( m_normalDiffType == Difference::Insert || m_normalDiffType == Difference::Change ) + for( ; m_diffIterator != m_diffLines.end() && m_normalHunkBodyAdded.exactMatch( *m_diffIterator ); ++m_diffIterator ) + { +// kDebug(8101) << "Line = " << *m_diffIterator << endl; + diff->addDestinationLine( m_normalHunkBodyAdded.cap( 1 ) ); + } + + return true; +} + +bool ParserBase::parseRCSHunkBody() +{ + return false; +} + +bool ParserBase::matchesUnifiedHunkLine( QString line ) const +{ + static const QChar context( ' ' ); + static const QChar added ( '+' ); + static const QChar removed( '-' ); + + QChar first = line[0]; + + return ( first == context || first == added || first == removed ); +} + +bool ParserBase::parseUnifiedHunkBody() +{ +// kDebug(8101) << "ParserBase::parseUnifiedHunkBody" << endl; + + int linenoA = 0, linenoB = 0; + bool wasNum; + + // Fetching the stuff we need from the hunkheader regexp that was parsed in parseUnifiedHunkHeader(); + linenoA = m_unifiedHunkHeader.cap( 1 ).toInt(); + int lineCountA = 1, lineCountB = 1; // an ommitted line count in the header implies a line count of 1 + if( !m_unifiedHunkHeader.cap( 3 ).isEmpty() ) + { + lineCountA = m_unifiedHunkHeader.cap( 3 ).toInt(&wasNum); + if( !wasNum ) + return false; + + // If a hunk is an insertion or deletion with no context, the line number given + // is the one before the hunk. this isn't what we want, so increment it to fix this. + if(lineCountA == 0) + linenoA++; + } + linenoB = m_unifiedHunkHeader.cap( 4 ).toInt(); + if( !m_unifiedHunkHeader.cap( 6 ).isEmpty() ) { + lineCountB = m_unifiedHunkHeader.cap( 6 ).toInt(&wasNum); + if( !wasNum ) + return false; + + if(lineCountB == 0) // see above + linenoB++; + } + QString function = m_unifiedHunkHeader.cap( 7 ); + + DiffHunk* hunk = new DiffHunk( linenoA, linenoB, function ); + m_currentModel->addHunk( hunk ); + + const QStringList::ConstIterator m_diffLinesEnd = m_diffLines.end(); + + const QString context = QString( " " ); + const QString added = QString( "+" ); + const QString removed = QString( "-" ); + + while( m_diffIterator != m_diffLinesEnd && matchesUnifiedHunkLine( *m_diffIterator ) && (lineCountA || lineCountB) ) + { + Difference* diff = new Difference( linenoA, linenoB ); + hunk->add( diff ); + + if( (*m_diffIterator).startsWith( context ) ) + { // context + for( ; m_diffIterator != m_diffLinesEnd && (*m_diffIterator).startsWith( context ) && (lineCountA || lineCountB); ++m_diffIterator ) + { + diff->addSourceLine( QString( *m_diffIterator ).remove( 0, 1 ) ); + diff->addDestinationLine( QString( *m_diffIterator ).remove( 0, 1 ) ); + linenoA++; + linenoB++; + --lineCountA; + --lineCountB; + } + } + else + { // This is a real difference, not context + for( ; m_diffIterator != m_diffLinesEnd && (*m_diffIterator).startsWith( removed ) && (lineCountA || lineCountB); ++m_diffIterator ) + { + diff->addSourceLine( QString( *m_diffIterator ).remove( 0, 1 ) ); + linenoA++; + --lineCountA; + } + for( ; m_diffIterator != m_diffLinesEnd && (*m_diffIterator).startsWith( added ) && (lineCountA || lineCountB); ++m_diffIterator ) + { + diff->addDestinationLine( QString( *m_diffIterator ).remove( 0, 1 ) ); + linenoB++; + --lineCountB; + } + if ( diff->sourceLineCount() == 0 ) + { + diff->setType( Difference::Insert ); +// kDebug(8101) << "Insert difference" << endl; + } + else if ( diff->destinationLineCount() == 0 ) + { + diff->setType( Difference::Delete ); +// kDebug(8101) << "Delete difference" << endl; + } + else + { + diff->setType( Difference::Change ); +// kDebug(8101) << "Change difference" << endl; + } + diff->determineInlineDifferences(); + m_currentModel->addDiff( diff ); + } + } + + return true; +} + +void ParserBase::checkHeader( const QRegExp& header ) +{ + if ( m_diffIterator != m_diffLines.end() + && !header.exactMatch( *m_diffIterator ) + && !m_diffIterator->startsWith("Index: ") /* SVN diff */ + && !m_diffIterator->startsWith("diff ") /* concatenated diff */) + m_malformed = true; +} + +DiffModelList* ParserBase::parseContext() +{ + while ( parseContextDiffHeader() ) + { + while ( parseContextHunkHeader() ) + parseContextHunkBody(); + if ( m_currentModel->differenceCount() > 0 ) + m_models->append( m_currentModel ); + checkHeader( m_contextDiffHeader1 ); + } + + m_models->sort(); + + if ( m_models->count() > 0 ) + { + return m_models; + } + else + { + delete m_models; + return 0L; + } +} + +DiffModelList* ParserBase::parseEd() +{ + while ( parseEdDiffHeader() ) + { + while ( parseEdHunkHeader() ) + parseEdHunkBody(); + if ( m_currentModel->differenceCount() > 0 ) + m_models->append( m_currentModel ); + } + + m_models->sort(); + + if ( m_models->count() > 0 ) + { + return m_models; + } + else + { + delete m_models; + return 0L; + } +} + +DiffModelList* ParserBase::parseNormal() +{ + while ( parseNormalDiffHeader() ) + { + while ( parseNormalHunkHeader() ) + parseNormalHunkBody(); + if ( m_currentModel->differenceCount() > 0 ) + m_models->append( m_currentModel ); + checkHeader( m_normalDiffHeader ); + } + + if ( m_singleFileDiff ) + { + while ( parseNormalHunkHeader() ) + parseNormalHunkBody(); + if ( m_currentModel->differenceCount() > 0 ) + m_models->append( m_currentModel ); + if ( m_diffIterator != m_diffLines.end() ) + m_malformed = true; + } + + m_models->sort(); + + if ( m_models->count() > 0 ) + { + return m_models; + } + else + { + delete m_models; + return 0L; + } +} + +DiffModelList* ParserBase::parseRCS() +{ + while ( parseRCSDiffHeader() ) + { + while ( parseRCSHunkHeader() ) + parseRCSHunkBody(); + if ( m_currentModel->differenceCount() > 0 ) + m_models->append( m_currentModel ); + } + + m_models->sort(); + + if ( m_models->count() > 0 ) + { + return m_models; + } + else + { + delete m_models; + return 0L; + } +} + +DiffModelList* ParserBase::parseUnified() +{ + while ( parseUnifiedDiffHeader() ) + { + while ( parseUnifiedHunkHeader() ) + parseUnifiedHunkBody(); +// kDebug(8101) << "New model ready to be analyzed..." << endl; +// kDebug(8101) << " differenceCount() == " << m_currentModel->differenceCount() << endl; + if ( m_currentModel->differenceCount() > 0 ) + m_models->append( m_currentModel ); + checkHeader( m_unifiedDiffHeader1 ); + } + + m_models->sort(); + + if ( m_models->count() > 0 ) + { + return m_models; + } + else + { + delete m_models; + return 0L; + } +} + diff --git a/libkomparediff2/parserbase.h b/libkomparediff2/parserbase.h new file mode 100644 index 00000000..b6ec269a --- /dev/null +++ b/libkomparediff2/parserbase.h @@ -0,0 +1,134 @@ +/************************************************************************** +** parserbase.h +** ------------- +** begin : Tue Jul 30 23:53:52 2002 +** Copyright 2002-2004 Otto Bruggeman +** Copyright 2010 Kevin Kofler +***************************************************************************/ +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 DIFF2_PARSERBASE_H +#define DIFF2_PARSERBASE_H + +#include + +#include "kompare.h" +#include "difference.h" +#include "diffmodellist.h" + +class QStringList; +class QString; + +namespace Diff2 +{ + +class KompareModelList; + +class ParserBase +{ +public: + ParserBase( const KompareModelList* list, const QStringList& diff ); + virtual ~ParserBase(); + +public: + enum Kompare::Format format() { return determineFormat(); }; + DiffModelList* parse( bool* malformed = 0 ); + +protected: + virtual bool parseContextDiffHeader(); + virtual bool parseEdDiffHeader(); + virtual bool parseNormalDiffHeader(); + virtual bool parseRCSDiffHeader(); + virtual bool parseUnifiedDiffHeader(); + + virtual bool parseContextHunkHeader(); + virtual bool parseEdHunkHeader(); + virtual bool parseNormalHunkHeader(); + virtual bool parseRCSHunkHeader(); + virtual bool parseUnifiedHunkHeader(); + + virtual bool parseContextHunkBody(); + virtual bool parseEdHunkBody(); + virtual bool parseNormalHunkBody(); + virtual bool parseRCSHunkBody(); + virtual bool parseUnifiedHunkBody(); + + virtual DiffModelList* parseContext(); + virtual DiffModelList* parseEd(); + virtual DiffModelList* parseNormal(); + virtual DiffModelList* parseRCS(); + virtual DiffModelList* parseUnified(); + +protected: // Helper methods to speed things up + bool matchesUnifiedHunkLine( QString line ) const; + void checkHeader( const QRegExp& header ); + +protected: + /** What is format of the diff */ + virtual enum Kompare::Format determineFormat(); + +protected: + // Regexps for context parsing + QRegExp m_contextDiffHeader1; + QRegExp m_contextDiffHeader2; + + QRegExp m_contextHunkHeader1; + QRegExp m_contextHunkHeader2; + QRegExp m_contextHunkHeader3; + + QRegExp m_contextHunkBodyRemoved; + QRegExp m_contextHunkBodyAdded; + QRegExp m_contextHunkBodyChanged; + QRegExp m_contextHunkBodyContext; + QRegExp m_contextHunkBodyLine; // Added for convenience + + // Regexps for normal parsing + QRegExp m_normalDiffHeader; + + QRegExp m_normalHunkHeaderAdded; + QRegExp m_normalHunkHeaderRemoved; + QRegExp m_normalHunkHeaderChanged; + + QRegExp m_normalHunkBodyRemoved; + QRegExp m_normalHunkBodyAdded; + QRegExp m_normalHunkBodyDivider; + + enum Difference::Type m_normalDiffType; + + // RegExps for rcs parsing + QRegExp m_rcsDiffHeader; + + // Regexps for unified parsing + QRegExp m_unifiedDiffHeader1; + QRegExp m_unifiedDiffHeader2; + + QRegExp m_unifiedHunkHeader; + + QRegExp m_unifiedHunkBodyAdded; + QRegExp m_unifiedHunkBodyRemoved; + QRegExp m_unifiedHunkBodyContext; + QRegExp m_unifiedHunkBodyLine; // Added for convenience + +protected: + const QStringList& m_diffLines; + DiffModel* m_currentModel; + DiffModelList* m_models; + QStringList::ConstIterator m_diffIterator; + + bool m_singleFileDiff; + bool m_malformed; + +protected: + const KompareModelList* m_list; +}; + +} // End of namespace Diff2 + +#endif diff --git a/libkomparediff2/perforceparser.cpp b/libkomparediff2/perforceparser.cpp new file mode 100644 index 00000000..60060332 --- /dev/null +++ b/libkomparediff2/perforceparser.cpp @@ -0,0 +1,221 @@ +/************************************************************************** +** perforceparser.cpp +** ------------------ +** begin : Sun Aug 4 15:05:35 2002 +** Copyright 2002-2004 Otto Bruggeman +***************************************************************************/ +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 "perforceparser.h" + +#include + +#include + +using namespace Diff2; + +PerforceParser::PerforceParser( const KompareModelList* list, const QStringList& diff ) : ParserBase( list, diff ) +{ + m_contextDiffHeader1.setPattern( "==== (.*) - (.*) ====\\n" ); + m_contextDiffHeader1.setMinimal( true ); + m_normalDiffHeader.setPattern ( "==== (.*) - (.*) ====\\n" ); + m_normalDiffHeader.setMinimal ( true ); + m_rcsDiffHeader.setPattern ( "==== (.*) - (.*) ====\\n" ); + m_rcsDiffHeader.setMinimal ( true ); + m_unifiedDiffHeader1.setPattern( "==== (.*) - (.*) ====\\n" ); + m_unifiedDiffHeader1.setMinimal( true ); +} + +PerforceParser::~PerforceParser() +{ +} + +enum Kompare::Format PerforceParser::determineFormat() +{ + kDebug(8101) << "Determining the format of the Perforce Diff" << endl; + + QRegExp unifiedRE( "^@@" ); + QRegExp contextRE( "^\\*{15}" ); + QRegExp normalRE ( "^\\d+(|,\\d+)[acd]\\d+(|,\\d+)" ); + QRegExp rcsRE ( "^[acd]\\d+ \\d+" ); + // Summary is not supported since it gives no useful parsable info + + QStringList::ConstIterator it = m_diffLines.begin(); + + while( it != m_diffLines.end() ) + { + if( it->indexOf( unifiedRE, 0 ) == 0 ) + { + kDebug(8101) << "Difflines are from a Unified diff..." << endl; + return Kompare::Unified; + } + else if( it->indexOf( contextRE, 0 ) == 0 ) + { + kDebug(8101) << "Difflines are from a Context diff..." << endl; + return Kompare::Context; + } + else if( it->indexOf( normalRE, 0 ) == 0 ) + { + kDebug(8101) << "Difflines are from a Normal diff..." << endl; + return Kompare::Normal; + } + else if( it->indexOf( rcsRE, 0 ) == 0 ) + { + kDebug(8101) << "Difflines are from a RCS diff..." << endl; + return Kompare::RCS; + } + ++it; + } + kDebug(8101) << "Difflines are from an unknown diff..." << endl; + return Kompare::UnknownFormat; +} + +bool PerforceParser::parseContextDiffHeader() +{ +// kDebug(8101) << "ParserBase::parseContextDiffHeader()" << endl; + bool result = false; + + QStringList::ConstIterator itEnd = m_diffLines.end(); + + QRegExp sourceFileRE ( "([^\\#]+)#(\\d+)" ); + QRegExp destinationFileRE( "([^\\#]+)#(|\\d+)" ); + + while ( m_diffIterator != itEnd ) + { + if ( m_contextDiffHeader1.exactMatch( *(m_diffIterator)++ ) ) + { +// kDebug(8101) << "Matched length Header1 = " << m_contextDiffHeader1.matchedLength() << endl; +// kDebug(8101) << "Matched string Header1 = " << m_contextDiffHeader1.cap( 0 ) << endl; +// kDebug(8101) << "First capture Header1 = " << m_contextDiffHeader1.cap( 1 ) << endl; +// kDebug(8101) << "Second capture Header1 = " << m_contextDiffHeader1.cap( 2 ) << endl; + + m_currentModel = new DiffModel(); + sourceFileRE.exactMatch( m_contextDiffHeader1.cap( 1 ) ); + destinationFileRE.exactMatch( m_contextDiffHeader1.cap( 2 ) ); + kDebug(8101) << "Matched length = " << sourceFileRE.matchedLength() << endl; + kDebug(8101) << "Matched length = " << destinationFileRE.matchedLength() << endl; + kDebug(8101) << "Captured texts = " << sourceFileRE.capturedTexts() << endl; + kDebug(8101) << "Captured texts = " << destinationFileRE.capturedTexts() << endl; + kDebug(8101) << "Source File : " << sourceFileRE.cap( 1 ) << endl; + kDebug(8101) << "Destination File : " << destinationFileRE.cap( 1 ) << endl; + m_currentModel->setSourceFile ( sourceFileRE.cap( 1 ) ); + m_currentModel->setDestinationFile( destinationFileRE.cap( 1 ) ); + + result = true; + + break; + } + else + { + kDebug(8101) << "Matched length = " << m_contextDiffHeader1.matchedLength() << endl; + kDebug(8101) << "Captured texts = " << m_contextDiffHeader1.capturedTexts() << endl; + } + } + + return result; +} + +bool PerforceParser::parseNormalDiffHeader() +{ + bool result = false; + + QStringList::ConstIterator itEnd = m_diffLines.end(); + + QRegExp sourceFileRE ( "([^\\#]+)#(\\d+)" ); + QRegExp destinationFileRE( "([^\\#]+)#(|\\d+)" ); + + while ( m_diffIterator != itEnd ) + { + kDebug(8101) << "Line = " << *m_diffIterator << endl; + kDebug(8101) << "String length = " << (*m_diffIterator).length() << endl; + if ( m_normalDiffHeader.exactMatch( *(m_diffIterator)++ ) ) + { + kDebug(8101) << "Matched length Header1 = " << m_normalDiffHeader.matchedLength() << endl; + kDebug(8101) << "Matched string Header1 = " << m_normalDiffHeader.cap( 0 ) << endl; + kDebug(8101) << "First capture Header1 = \"" << m_normalDiffHeader.cap( 1 ) << "\"" << endl; + kDebug(8101) << "Second capture Header1 = \"" << m_normalDiffHeader.cap( 2 ) << "\"" << endl; + + m_currentModel = new DiffModel(); + sourceFileRE.exactMatch( m_normalDiffHeader.cap( 1 ) ); + destinationFileRE.exactMatch( m_normalDiffHeader.cap( 2 ) ); + kDebug(8101) << "Matched length = " << sourceFileRE.matchedLength() << endl; + kDebug(8101) << "Matched length = " << destinationFileRE.matchedLength() << endl; + kDebug(8101) << "Captured texts = " << sourceFileRE.capturedTexts() << endl; + kDebug(8101) << "Captured texts = " << destinationFileRE.capturedTexts() << endl; + kDebug(8101) << "Source File : " << sourceFileRE.cap( 1 ) << endl; + kDebug(8101) << "Destination File : " << destinationFileRE.cap( 1 ) << endl; + m_currentModel->setSourceFile ( sourceFileRE.cap( 1 ) ); + m_currentModel->setDestinationFile( destinationFileRE.cap( 1 ) ); + + result = true; + + break; + } + else + { + kDebug(8101) << "Matched length = " << m_normalDiffHeader.matchedLength() << endl; + kDebug(8101) << "Captured texts = " << m_normalDiffHeader.capturedTexts() << endl; + } + } + + return result; +} + +bool PerforceParser::parseRCSDiffHeader() +{ + return false; +} + +bool PerforceParser::parseUnifiedDiffHeader() +{ + bool result = false; + + QStringList::ConstIterator itEnd = m_diffLines.end(); + + QRegExp sourceFileRE ( "([^\\#]+)#(\\d+)" ); + QRegExp destinationFileRE( "([^\\#]+)#(|\\d+)" ); + + while ( m_diffIterator != itEnd ) + { +// kDebug(8101) << "Line = " << *m_diffIterator << endl; +// kDebug(8101) << "String length = " << (*m_diffIterator).length() << endl; + if ( m_unifiedDiffHeader1.exactMatch( *(m_diffIterator)++ ) ) + { +// kDebug(8101) << "Matched length Header1 = " << m_unifiedDiffHeader1.matchedLength() << endl; +// kDebug(8101) << "Matched string Header1 = " << m_unifiedDiffHeader1.cap( 0 ) << endl; +// kDebug(8101) << "First capture Header1 = \"" << m_unifiedDiffHeader1.cap( 1 ) << "\"" << endl; +// kDebug(8101) << "Second capture Header1 = \"" << m_unifiedDiffHeader1.cap( 2 ) << "\"" << endl; + + m_currentModel = new DiffModel(); + sourceFileRE.exactMatch( m_unifiedDiffHeader1.cap( 1 ) ); + destinationFileRE.exactMatch( m_unifiedDiffHeader1.cap( 2 ) ); +// kDebug(8101) << "Matched length = " << sourceFileRE.matchedLength() << endl; +// kDebug(8101) << "Matched length = " << destinationFileRE.matchedLength() << endl; +// kDebug(8101) << "Captured texts = " << sourceFileRE.capturedTexts() << endl; +// kDebug(8101) << "Captured texts = " << destinationFileRE.capturedTexts() << endl; +// kDebug(8101) << "Source File : " << sourceFileRE.cap( 1 ) << endl; +// kDebug(8101) << "Destination File : " << destinationFileRE.cap( 1 ) << endl; + m_currentModel->setSourceFile ( sourceFileRE.cap( 1 ) ); + m_currentModel->setDestinationFile( destinationFileRE.cap( 1 ) ); + + result = true; + + break; + } + else + { +// kDebug(8101) << "Matched length = " << m_unifiedDiffHeader1.matchedLength() << endl; +// kDebug(8101) << "Captured texts = " << m_unifiedDiffHeader1.capturedTexts() << endl; + } + } + + return result; +} + diff --git a/libkomparediff2/perforceparser.h b/libkomparediff2/perforceparser.h new file mode 100644 index 00000000..c19e281b --- /dev/null +++ b/libkomparediff2/perforceparser.h @@ -0,0 +1,42 @@ +/************************************************************************** +** perforceparser.h +** ---------------- +** begin : Sun Sep 8 20:58:59 2002 +** Copyright 2002-2004 Otto Bruggeman +***************************************************************************/ +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 PERFORCE_PARSER_H +#define PERFORCE_PARSER_H + +#include "parserbase.h" + +namespace Diff2 +{ + +class PerforceParser : public ParserBase +{ +public: + PerforceParser( const KompareModelList* list, const QStringList& diff ); + virtual ~PerforceParser(); + +protected: + virtual bool parseContextDiffHeader(); + virtual bool parseNormalDiffHeader(); + virtual bool parseRCSDiffHeader(); + virtual bool parseUnifiedDiffHeader(); + +protected: + virtual enum Kompare::Format determineFormat(); +}; + +} // End of namespace Diff2 + +#endif diff --git a/libkomparediff2/settingsbase.cpp b/libkomparediff2/settingsbase.cpp new file mode 100644 index 00000000..a68c3bb2 --- /dev/null +++ b/libkomparediff2/settingsbase.cpp @@ -0,0 +1,40 @@ +/*************************************************************************** + settingsbase.cpp + ---------------- + begin : Sun Mar 4 2001 + Copyright 2001 Otto Bruggeman + Copyright 2001 John Firebaugh +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 "settingsbase.h" + +#include + +SettingsBase::SettingsBase( QWidget* parent ) : QObject( parent ) +{ + +} + +SettingsBase::~SettingsBase() +{ + +} + +void SettingsBase::loadSettings( KConfig* /* config */ ) +{ +} + +void SettingsBase::saveSettings( KConfig* /* config */ ) +{ +} + +#include "settingsbase.moc" diff --git a/libkomparediff2/settingsbase.h b/libkomparediff2/settingsbase.h new file mode 100644 index 00000000..ee1284b9 --- /dev/null +++ b/libkomparediff2/settingsbase.h @@ -0,0 +1,41 @@ +/*************************************************************************** + settingsbase.h + -------------- + begin : Sun Mar 4 2001 + Copyright 2001 Otto Bruggeman + Copyright 2001 John Firebaugh +****************************************************************************/ + +/*************************************************************************** +** +** This program is free software; you can redistribute it and/or modify +** it under the terms of the 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 SETTINGSBASE_H +#define SETTINGSBASE_H + +#include + +#include "kompare.h" +#include "diff2export.h" + +class QWidget; +class KConfig; + +class DIFF2_EXPORT SettingsBase : public QObject +{ +Q_OBJECT +public: + SettingsBase( QWidget* parent ); + ~SettingsBase(); + +public: + virtual void loadSettings( KConfig* config ); + virtual void saveSettings( KConfig* config ); +}; + +#endif diff --git a/libkomparediff2/stringlistpair.cpp b/libkomparediff2/stringlistpair.cpp new file mode 100644 index 00000000..3ef34d24 --- /dev/null +++ b/libkomparediff2/stringlistpair.cpp @@ -0,0 +1,97 @@ +/* + * This file is part of KDevelop + * Copyright 2011 Dmitry Risenberg + * + * 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 General Public + * 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 "stringlistpair.h" + +#include + +using namespace Diff2; + +unsigned int StringListPair::lengthFirst() const +{ + return m_lengthFirst; +} + +unsigned int StringListPair::lengthSecond() const +{ + return m_lengthSecond; +} + +MarkerList StringListPair::markerListFirst() const +{ + return m_markersFirst; +} + +MarkerList StringListPair::markerListSecond() const +{ + return m_markersSecond; +} + +void StringListPair::prependFirst(Marker* marker) +{ + m_markersFirst.prepend(marker); +} + +void StringListPair::prependSecond(Marker* marker) +{ + m_markersSecond.prepend(marker); +} + +StringListPair::StringListPair(const QStringList& first, const QStringList& second) + : m_first(first), m_second(second) +{ + // Do not forget about 1 virtual element - see LevenshteinTable + m_lengthFirst = first.length() + 1; + m_lengthSecond = second.length() + 1; + + m_hashesFirst = new unsigned int[m_lengthFirst]; + m_hashesSecond = new unsigned int[m_lengthSecond]; + + m_hashesFirst[0] = qHash(QString("")); + for (unsigned int i = 1; i < m_lengthFirst; ++i) { + m_hashesFirst[i] = qHash(first[i - 1]); + } + m_hashesSecond[0] = qHash(QString("")); + for (unsigned int i = 1; i < m_lengthSecond; ++i) { + m_hashesSecond[i] = qHash(second[i - 1]); + } +} + +StringListPair::~StringListPair() +{ + delete[] m_hashesFirst; + delete[] m_hashesSecond; +} + +bool StringListPair::equal(unsigned int firstIndex, unsigned int secondIndex) const +{ + if (m_hashesFirst[firstIndex] != m_hashesSecond[secondIndex]) { + return false; + } + if (firstIndex == 0 || secondIndex == 0) { + return firstIndex == 0 && secondIndex == 0; + } + return m_first[firstIndex - 1] == m_second[secondIndex - 1]; +} + +bool StringListPair::needFineGrainedOutput(unsigned int) const +{ + return true; +} diff --git a/libkomparediff2/stringlistpair.h b/libkomparediff2/stringlistpair.h new file mode 100644 index 00000000..26c2380b --- /dev/null +++ b/libkomparediff2/stringlistpair.h @@ -0,0 +1,56 @@ +/* + * This file is part of KDevelop + * Copyright 2011 Dmitry Risenberg + * + * 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 General Public + * 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 STRINGLISTPAIR_H +#define STRINGLISTPAIR_H + +#include +#include "marker.h" + +namespace Diff2 { + +class StringListPair { +public: + StringListPair(const QStringList& first, const QStringList& second); + ~StringListPair(); + bool equal(unsigned int firstIndex, unsigned int secondIndex) const; + unsigned int lengthFirst() const; + unsigned int lengthSecond() const; + MarkerList markerListFirst() const; + MarkerList markerListSecond() const; + void prependFirst(Marker* marker); + void prependSecond(Marker* marker); + bool needFineGrainedOutput(unsigned int difference) const; + + const static bool allowReplace = false; +private: + const QStringList m_first; + const QStringList m_second; + unsigned int m_lengthFirst; + unsigned int m_lengthSecond; + unsigned int* m_hashesFirst; + unsigned int* m_hashesSecond; + MarkerList m_markersFirst; + MarkerList m_markersSecond; +}; + +} + +#endif // STRINGLISTPAIR_H diff --git a/libkomparediff2/tests/CMakeLists.txt b/libkomparediff2/tests/CMakeLists.txt new file mode 100644 index 00000000..1f0200d3 --- /dev/null +++ b/libkomparediff2/tests/CMakeLists.txt @@ -0,0 +1,40 @@ +include_directories(..) + +set( EXECUTABLE_OUTPUT_PATH ${CMAKE_CURRENT_BINARY_DIR} ) + +set(interactiveDiffTest_SRCS + interactivedifftest.cpp + ../perforceparser.cpp + ../cvsdiffparser.cpp + ../difference.cpp + ../diffhunk.cpp + ../diffmodel.cpp + ../diffmodellist.cpp + ../diffparser.cpp + ../komparemodellist.cpp + ../kompareprocess.cpp + ../parser.cpp + ../parserbase.cpp + ../stringlistpair.cpp +) + +kde4_add_unit_test(interactiveDiffTest ${interactiveDiffTest_SRCS}) +set_target_properties(interactiveDiffTest PROPERTIES COMPILE_FLAGS "-DDIFF2_EXPORT=") +target_link_libraries(interactiveDiffTest + ${QT_QTTEST_LIBRARY} + ${KDE4_KDECORE_LIBS} + ${KDE4_KPARTS_LIBS} +) + +set(levenshteinTest_SRCS + levenshteintest.cpp + ../difference.cpp + ../stringlistpair.cpp +) + +kde4_add_unit_test(levenshteintest ${levenshteinTest_SRCS}) +set_target_properties(levenshteintest PROPERTIES COMPILE_FLAGS "-DDIFF2_EXPORT=") +target_link_libraries(levenshteintest + ${QT_QTTEST_LIBRARY} + ${QT_QTCORE_LIBRARY} +) diff --git a/libkomparediff2/tests/interactivedifftest.cpp b/libkomparediff2/tests/interactivedifftest.cpp new file mode 100644 index 00000000..45bc8940 --- /dev/null +++ b/libkomparediff2/tests/interactivedifftest.cpp @@ -0,0 +1,819 @@ +/* + * This file is part of KDevelop + * Copyright 2011 Dmitry Risenberg + * + * 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 General Public + * 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 "interactivedifftest.h" + +#include +#include "../diffmodel.h" +#include "../parser.h" + +using namespace Diff2; + +typedef QHash > DifferenceHash; +Q_DECLARE_METATYPE(DifferenceHash); +typedef QHash > LineNumberHash; +Q_DECLARE_METATYPE(LineNumberHash); + +void InteractiveDiffTest::CompareDifferenceStringList(const DifferenceStringList& actual, const QStringList& expected) +{ + DifferenceStringListConstIterator actualIter; + QStringList::const_iterator expectedIter; + for(actualIter = actual.constBegin(), expectedIter = expected.constBegin(); actualIter != actual.constEnd() && expectedIter != expected.constEnd(); ++actualIter, ++expectedIter) { + QCOMPARE((*actualIter)->string(), *expectedIter); + } + if (actualIter != actual.constEnd()) { + QFAIL(QString("Actual has too many items, starting with '%1', line %2").arg((*actualIter)->string()).arg(actualIter - actual.constBegin()).toAscii()); + } + if (expectedIter != expected.constEnd()) { + QFAIL(QString("Actual has too few items, no match for '%1', line %2").arg(*expectedIter).arg(expectedIter - expected.constBegin()).toAscii()); + } +} + +// The most basic test - something is actually working +void InteractiveDiffTest::testOneLineChange() +{ + DiffModel* model = new DiffModel(); + QStringList newLines; + newLines << "newline\n"; + QStringList oldLines; + oldLines << "oldline\n"; + model->linesChanged(oldLines, newLines, 2); + QCOMPARE(model->differences()->size(), 1); + + QCOMPARE(model->differenceCount(), 1); + const Difference* diff = model->differenceAt(0); + CompareDifferenceStringList(diff->sourceLines(), oldLines); + CompareDifferenceStringList(diff->destinationLines(), newLines); + QCOMPARE(diff->type(), int(Difference::Change)); +} + +void InteractiveDiffTest::testSameLine() +{ + DiffModel* model = new DiffModel(); + QStringList newLines; + newLines << "oldline2\n"; + QStringList oldLines; + oldLines << "oldline1\n" << "oldline2\n"; + model->linesChanged(oldLines, newLines, 2); + + QCOMPARE(model->differenceCount(), 1); + const Difference* diff = model->differenceAt(0); + CompareDifferenceStringList(diff->sourceLines(), QStringList() << "oldline1\n"); + CompareDifferenceStringList(diff->destinationLines(), QStringList()); + QCOMPARE(diff->type(), int(Difference::Delete)); +} + +void InteractiveDiffTest::testDifferenceContents() +{ + QFETCH(QStringList, patch); + Parser parser(0); + DiffModelList* models = parser.parse(patch); + QCOMPARE(models->size(), 1); + DiffModel* model = models->at(0); + + QFETCH(QStringList, oldLines); + QFETCH(QStringList, newLines); + QFETCH(int, editLineNumber); + QFETCH(bool, isAlreadyApplied); + model->applyAllDifferences(isAlreadyApplied); + model->linesChanged(oldLines, newLines, editLineNumber); + QFETCH(int, expectedDifferenceCount); + QCOMPARE(model->differenceCount(), expectedDifferenceCount); + + QFETCH(DifferenceHash, expectedDifferences); + for (DifferenceHash::ConstIterator iter = expectedDifferences.constBegin(); iter != expectedDifferences.constEnd(); ++iter) { + const Difference* diff = model->differenceAt(iter.key()); + CompareDifferenceStringList(diff->sourceLines(), iter.value().first); + CompareDifferenceStringList(diff->destinationLines(), iter.value().second); + } +} + +void InteractiveDiffTest::testDifferenceContents_data() +{ + QTest::addColumn("patch"); + QTest::addColumn("oldLines"); // lines that are replaced + QTest::addColumn("newLines"); // replacement lines + QTest::addColumn("editLineNumber"); + QTest::addColumn("isAlreadyApplied"); + QTest::addColumn("expectedDifferenceCount"); + QTest::addColumn("expectedDifferences"); + + { + QStringList patch; + patch << + "--- file1\t2011-01-01 20:23:45.000000000 +0300\n" << + "+++ file2\t2011-01-01 20:24:02.000000000 +0300\n" << + "@@ -1,5 +1,5 @@\n" << + " abcd\n" << + "-delete1\n" << + "+insert1\n" << + " efgh\n" << + "-delete2\n" << + "+insert2\n" << + " ijkl\n"; + + QStringList newLines; + newLines << "newline1\n" << "newline2\n"; + QStringList oldLines; + oldLines << "efgh\n"; + QStringList sourceLines; + sourceLines << "delete1\n" << "efgh\n" << "delete2\n"; + QStringList destinationLines; + destinationLines << "insert1\n" << "newline1\n" << "newline2\n" << "insert2\n"; + DifferenceHash expectedDifferences; + expectedDifferences.insert(0, qMakePair(sourceLines, destinationLines)); + + QTest::newRow("Merge adjacent differences") << patch << oldLines << newLines << 3 << true << 1 << expectedDifferences; + } + { + QStringList patch; + patch << + "--- file1\t2011-01-01 20:23:45.000000000 +0300\n" << + "+++ file2\t2011-01-01 20:24:02.000000000 +0300\n" << + "@@ -1,4 +1,4 @@\n" << + " abcd\n" << + "-delete1\n" << + "-delete2\n" << + "+insert1\n" << + "+insert2\n" << + " efgh\n"; + + QStringList newLines; + newLines << "newline1\n"; + QStringList oldLines; + oldLines << "efgh\n"; + QStringList sourceLines; + sourceLines << "delete1\n" << "delete2\n" << "efgh\n"; + QStringList destinationLines; + destinationLines << "insert1\n" << "insert2\n" << "newline1\n"; + DifferenceHash expectedDifferences; + expectedDifferences.insert(0, qMakePair(sourceLines, destinationLines)); + + // Append a line to a multiline diff + QTest::newRow("Append multiline") << patch << oldLines << newLines << 4 << true << 1 << expectedDifferences; + } + { + QStringList patch; + patch << + "--- file1\t2011-01-01 20:23:45.000000000 +0300\n" << + "+++ file2\t2011-01-01 20:24:02.000000000 +0300\n" << + "@@ -1,3 +1,3 @@\n" << + " abcd\n" << + "-delete1\n" << + "+insert1\n" << + " efgh\n"; + + QStringList newLines; + newLines << "delete1\n"; + QStringList oldLines; + oldLines << "insert1\n"; + QTest::newRow("Revert existing difference") << patch << oldLines << newLines << 2 << true << 0 << DifferenceHash(); + } + { + QStringList patch; + patch << + "--- file1\t2011-01-01 20:23:45.000000000 +0300\n" << + "+++ file2\t2011-01-01 20:24:02.000000000 +0300\n" << + "@@ -1,3 +1,2 @@\n" << + " abcd\n" << + "-delete1\n" << + " efgh\n"; + + QStringList newLines; + newLines << "abcd\n" << "delete1\n"; + QStringList oldLines; + oldLines << "abcd\n"; + QTest::newRow("Revert deletion") << patch << oldLines << newLines << 1 << true << 0 << DifferenceHash(); + } + { + QStringList patch; + patch << + "--- file1\t2011-01-01 20:23:45.000000000 +0300\n" << + "+++ file2\t2011-01-01 20:24:02.000000000 +0300\n" << + "@@ -1,4 +1,5 @@\n" << + " abcd\n" << + "-delete1\n" << + "-delete2\n" << + "+insert1\n" << + "+insert2\n" << + "+insert3\n" << + " efgh\n"; + + QStringList newLines; + newLines << "delete2\n"; + QStringList oldLines; + oldLines << "insert2\n"; + DifferenceHash expectedDifferences; + expectedDifferences.insert(0, qMakePair(QStringList() << "delete1\n", QStringList() << "insert1\n")); + expectedDifferences.insert(1, qMakePair(QStringList(), QStringList() << "insert3\n")); + + QTest::newRow("Partial reversion") << patch << oldLines << newLines << 3 << true << 2 << expectedDifferences; + } + { + QStringList patch; + patch << + "--- file1\t2011-01-01 20:23:45.000000000 +0300\n" << + "+++ file2\t2011-01-01 20:24:02.000000000 +0300\n" << + "@@ -1,4 +1,4 @@\n" << + " abcd\n" << + "-delete1\n" << + "-delete2\n" << + "+insert1\n" << + "+insert2\n" << + " efgh\n"; + + QStringList newLines; + newLines << "newline1\n" << "newline2\n"; + QStringList oldLines; + oldLines << "abcd\n" << "insert1\n" << "insert2\n" << "efgh\n"; + QStringList sourceLines; + sourceLines << "abcd\n" << "delete1\n" << "delete2\n" << "efgh\n"; + QStringList destinationLines; + destinationLines << "newline1\n" << "newline2\n"; + DifferenceHash expectedDifferences; + expectedDifferences.insert(0, qMakePair(sourceLines, destinationLines)); + + // The first exisiting difference inside the edit + QTest::newRow("First inside") << patch << oldLines << newLines << 1 << true << 1 << expectedDifferences; + } + { + QStringList patch; + patch << + "--- file1\t2011-01-01 20:23:45.000000000 +0300\n" << + "+++ file2\t2011-01-01 20:24:02.000000000 +0300\n" << + "@@ -1,4 +1,4 @@\n" << + " abcd\n" << + "-delete1\n" << + "-delete2\n" << + "+insert1\n" << + "+insert2\n" << + " efgh\n"; + + QStringList newLines; + newLines << "newline1\n" << "newline2\n"; + QStringList oldLines; + oldLines << "insert2\n" << "efgh\n"; + QStringList sourceLines; + sourceLines << "delete1\n" << "delete2\n" << "efgh\n"; + QStringList destinationLines; + destinationLines << "insert1\n" << "newline1\n" << "newline2\n"; + DifferenceHash expectedDifferences; + expectedDifferences.insert(0, qMakePair(sourceLines, destinationLines)); + + // The first existing difference intersects with the edit + QTest::newRow("First intersects") << patch << oldLines << newLines << 3 << true << 1 << expectedDifferences; + } + { + QStringList patch; + patch << + "--- file1\t2011-01-01 20:23:45.000000000 +0300\n" << + "+++ file2\t2011-01-01 20:24:02.000000000 +0300\n" << + "@@ -1,2 +1,3 @@\n" << + " abcd\n" << + "+\n" << + " efgh\n"; + + QStringList newLines; + newLines << "a\n"; + QStringList oldLines; + oldLines << "\n"; + DifferenceHash expectedDifferences; + expectedDifferences.insert(0, qMakePair(QStringList(), QStringList() << "a\n")); + + QTest::newRow("Replace empty line") << patch << oldLines << newLines << 2 << true << 1 << expectedDifferences; + } + { + QStringList patch; + patch << + "--- file1\t2011-01-01 20:23:45.000000000 +0300\n" << + "+++ file2\t2011-01-01 20:24:02.000000000 +0300\n" << + "@@ -1,3 +1,3 @@\n" << + " abcd\n" << + "+insert1\n" << + "+insert2\n" << + "+insert3\n" << + "+insert4\n" << + "+insert5\n" << + " efgh\n" << + "@@ -10,3 +15,3 @@\n" << + " abcd\n" << + "-delete1\n" << + "+insert1\n" << + " efgh\n"; + + QStringList newLines; + newLines << "newline1\n"; + QStringList oldLines; + oldLines << "delete1\n"; + DifferenceHash expectedDifferences; + expectedDifferences.insert(1, qMakePair(QStringList() << "delete1\n", QStringList() << "newline1\n")); + + QTest::newRow("Replace line in source") << patch << oldLines << newLines << 11 << false << 2 << expectedDifferences; + } +} + +void InteractiveDiffTest::testLineNumbers_data() +{ + QTest::addColumn("patch"); + QTest::addColumn("oldLines"); // lines that are replaced + QTest::addColumn("newLines"); // replacement lines + QTest::addColumn("editLineNumber"); + QTest::addColumn("expectedDifferenceCount"); + QTest::addColumn("expectedLineNumbers"); + + { + QStringList patch; + patch << + "--- file1\t2011-01-01 20:23:45.000000000 +0300\n" << + "+++ file2\t2011-01-01 20:24:02.000000000 +0300\n" << + "@@ -1,4 +1,6 @@\n" << + " abcd\n" << + "-delete1\n" << + "-delete2\n" << + "+insert1\n" << + "+insert2\n" << + "+insert3\n" << + "+insert4\n" << + " efgh\n" << + "@@ -15,3 +17,4 @@\n" << + " abcd\n" << + "-delete1\n" << + "+insert1\n" << + "+insert2\n" << + " efgh\n"; + + QStringList newLines; + newLines << "newline1\n" << "newline2\n" << "newline2\n"; + QStringList oldLines; + oldLines << "oldline1\n"; + LineNumberHash expectedLineNumbers; + expectedLineNumbers.insert(0, qMakePair(2, 2)); + expectedLineNumbers.insert(2, qMakePair(16, 20)); + QTest::newRow("Update existing line numbers") << patch << oldLines << newLines << 10 << 3 << expectedLineNumbers; + } + { + QStringList patch; + patch << + "--- file1\t2011-01-01 20:23:45.000000000 +0300\n" << + "+++ file2\t2011-01-01 20:24:02.000000000 +0300\n" << + "@@ -1,4 +1,6 @@\n" << + " abcd\n" << + "-delete1\n" << + "-delete2\n" << + "+insert1\n" << + "+insert2\n" << + "+insert3\n" << + "+insert4\n" << + " efgh\n" << + "@@ -15,3 +17,4 @@\n" << + " abcd\n" << + "-delete1\n" << + "+insert1\n" << + "+insert2\n" << + " efgh\n"; + + QStringList newLines; + newLines << "newline1\n"; + QStringList oldLines; + oldLines << "oldline1\n"; + LineNumberHash expectedLineNumbers; + expectedLineNumbers.insert(2, qMakePair(22, 25)); + + // Line numbers assigned to new difference when it is inserted after all existing differences + QTest::newRow("Last edit line number") << patch << oldLines << newLines << 25 << 3 << expectedLineNumbers; + } + { + QStringList patch; + patch << + "--- file1\t2011-01-01 20:23:45.000000000 +0300\n" << + "+++ file2\t2011-01-01 20:24:02.000000000 +0300\n" << + "@@ -1,4 +1,6 @@\n" << + " abcd\n" << + "-delete1\n" << + "-delete2\n" << + "+insert1\n" << + "+insert2\n" << + "+insert3\n" << + "+insert4\n" << + " efgh\n" << + "@@ -15,3 +17,4 @@\n" << + " abcd\n" << + "-delete1\n" << + "+insert1\n" << + "+insert2\n" << + " efgh\n"; + + QStringList newLines; + newLines << "newline1\n"; + QStringList oldLines; + oldLines << "oldline1\n"; + LineNumberHash expectedLineNumbers; + expectedLineNumbers.insert(1, qMakePair(11, 13)); + + // Line numbers assigned to new difference when it is inserted between existing differences + QTest::newRow("Middle edit line number") << patch << oldLines << newLines << 13 << 3 << expectedLineNumbers; + } + { + QStringList patch; + patch << + "--- file1\t2011-01-01 20:23:45.000000000 +0300\n" << + "+++ file2\t2011-01-01 20:24:02.000000000 +0300\n" << + "@@ -10,4 +10,4 @@\n" << + " abcd\n" << + "-delete1\n" << + "-delete2\n" << + "+insert1\n" << + "+insert2\n" << + " efgh\n"; + + QStringList newLines; + newLines << "newline1\n"; + QStringList oldLines; + oldLines << "oldline1\n"; + LineNumberHash expectedLineNumbers; + expectedLineNumbers.insert(0, qMakePair(5, 5)); + + // Line numbers assigned to new difference when it is inserted before all existing differences + QTest::newRow("First edit line number") << patch << oldLines << newLines << 5 << 2 << expectedLineNumbers; + } + { + QStringList patch; + patch << + "--- file1\t2011-01-01 20:23:45.000000000 +0300\n" << + "+++ file2\t2011-01-01 20:24:02.000000000 +0300\n" << + "@@ -1,3 +1,4 @@\n" << + " abcd\n" << + "-delete1\n" << + "+insert1\n" << + "+insert2\n" << + " efgh\n" << + "@@ -11,4 +12,5 @@\n" << + " abcd\n" << + "-delete2\n" << + "-delete3\n" << + "+insert3\n" << + "+insert4\n" << + "+insert5\n" << + " efgh\n" << + "@@ -21,4 +23,3 @@\n" << + " abcd\n" << + "-delete4\n" << + "-delete5\n" << + "+insert6\n" << + " efgh\n"; + + QStringList newLines; + newLines << "delete2\n"; + QStringList oldLines; + oldLines << "insert4\n"; + LineNumberHash expectedLineNumbers; + expectedLineNumbers.insert(0, qMakePair(2, 2)); + expectedLineNumbers.insert(1, qMakePair(12, 13)); + expectedLineNumbers.insert(2, qMakePair(13, 15)); + expectedLineNumbers.insert(3, qMakePair(22, 24)); + + QTest::newRow("Partial reversion") << patch << oldLines << newLines << 14 << 4 << expectedLineNumbers; + } +} + +void InteractiveDiffTest::testLineNumbers() +{ + QFETCH(QStringList, patch); + Parser parser(0); + DiffModelList* models = parser.parse(patch); + QCOMPARE(models->size(), 1); + DiffModel* model = models->at(0); + model->applyAllDifferences(true); + + QFETCH(QStringList, oldLines); + QFETCH(QStringList, newLines); + QFETCH(int, editLineNumber); + model->linesChanged(oldLines, newLines, editLineNumber); + QFETCH(int, expectedDifferenceCount); + QCOMPARE(model->differenceCount(), expectedDifferenceCount); + + QFETCH(LineNumberHash, expectedLineNumbers); + for (LineNumberHash::ConstIterator iter = expectedLineNumbers.constBegin(); iter != expectedLineNumbers.constEnd(); ++iter) { + const Difference* diff = model->differenceAt(iter.key()); + QCOMPARE(diff->sourceLineNumber(), iter.value().first); + QCOMPARE(diff->trackingDestinationLineNumber(), iter.value().second); + } +} + +// When the new diff and an existing unapplied one are on neighbour lines, do not merge the unapplied with the new. +void InteractiveDiffTest::testAppliedTouch() +{ + Difference* first = new Difference(2, 2); + first->addSourceLine(QString("delete1")); + first->addDestinationLine(QString("insert1")); + first->apply(false); + Difference* second = new Difference(4, 4); + second->addSourceLine(QString("delete2")); + second->addDestinationLine(QString("insert2")); + second->apply(false); + DiffModel model; + model.addDiff(first); + model.addDiff(second); + model.linesChanged(QStringList() << "oldline\n", QStringList() << "newline\n", 3); + QCOMPARE(model.differenceCount(), 3); + QCOMPARE(model.differenceAt(0), first); + QCOMPARE(model.differenceAt(2), second); +} + +// When the new diff and an existing unapplied one intersect, the unapplied one should be removed +void InteractiveDiffTest::testAppliedIntersect() +{ + Difference* first = new Difference(2, 2); + first->addSourceLine(QString("delete1")); + first->addSourceLine(QString("delete2")); + first->addDestinationLine(QString("insert1")); + first->addDestinationLine(QString("insert2")); + first->apply(false); + Difference* second = new Difference(5, 5); + second->addSourceLine(QString("delete3")); + second->addSourceLine(QString("delete4")); + second->addDestinationLine(QString("insert3")); + second->addDestinationLine(QString("insert4")); + second->apply(false); + DiffModel model; + model.addDiff(first); + model.addDiff(second); + QStringList removedLines; + removedLines << "delete2\n" << "oldline1\n" << "delete3\n"; + QStringList insertedLines; + insertedLines << "newline1\n"; + model.linesChanged(removedLines, insertedLines, 3); + QCOMPARE(model.differenceCount(), 1); + const Difference* newDiff = model.differenceAt(0); + QCOMPARE(newDiff->applied(), true); + QCOMPARE(newDiff->sourceLineNumber(), 3); + QCOMPARE(newDiff->trackingDestinationLineNumber(), 3); + CompareDifferenceStringList(newDiff->sourceLines(), removedLines); + CompareDifferenceStringList(newDiff->destinationLines(), insertedLines); +} + +void InteractiveDiffTest::testExistingAndApplied() +{ + Difference* first = new Difference(2, 2); + first->addSourceLine(QString("delete1")); + first->addDestinationLine(QString("insert1")); + first->apply(true); + Difference* second = new Difference(3, 3); + second->addSourceLine(QString("delete2")); + second->addDestinationLine(QString("insert2")); + second->apply(false); + DiffModel model; + model.addDiff(first); + model.addDiff(second); + QStringList removedLines; + removedLines << "delete1\n"; + QStringList insertedLines; + insertedLines << "newline1\n"; + model.linesChanged(removedLines, insertedLines, 2); + QCOMPARE(model.differenceCount(), 2); + QVERIFY(model.differenceAt(0)->applied()); + QVERIFY(!model.differenceAt(1)->applied()); +} + +void InteractiveDiffTest::testOneLineDeletionUnapplied() +{ + Difference* unappliedDeletion = new Difference(1, 1); + unappliedDeletion->addSourceLine("delete1\n"); + unappliedDeletion->apply(false); + DiffModel model; + model.addDiff(unappliedDeletion); + QStringList removedLines; + removedLines << "delete1\n"; + QStringList insertedLines; + insertedLines << "newline1\n"; + model.linesChanged(removedLines, insertedLines, 1); + QCOMPARE(model.differenceCount(), 1); + const Difference* actual = model.differenceAt(0); + CompareDifferenceStringList(actual->sourceLines(), removedLines); + CompareDifferenceStringList(actual->destinationLines(), insertedLines); +} + +void InteractiveDiffTest::testApplyUnapply() +{ + QStringList patch; + patch << + "--- file1\t2011-01-01 20:23:45 +0300\n" << + "+++ file2\t2011-01-01 20:24:02 +0300\n" << + "@@ -1,3 +1,4 @@\n" << + " line1\n" << + "-delete1\n" << + "+insert1\n" << + "+insert2\n" << + " line2\n" << + "@@ -11,4 +12,5 @@\n" << + " line3\n" << + "-delete2\n" << + "-delete3\n" << + "+insert3\n" << + "+insert4\n" << + "+insert5\n" << + " line4\n" << + "@@ -21,4 +23,2 @@\n" << + " line5\n" << + "-delete4\n" << + "-delete5\n" << + " line6\n" << + "@@ -31,3 +31,3 @@\n" << + " line7\n" << + "-delete6\n" << + "+insert6\n" << + " line8\n"; + Parser parser(0); + DiffModelList* models = parser.parse(patch); + QCOMPARE(models->size(), 1); + DiffModel* model = models->at(0); + QCOMPARE(model->differenceCount(), 4); + model->applyAllDifferences(true); + + foreach( Difference* diff, *model->differences() ) + { + QVERIFY(diff->applied()); + } + model->applyAllDifferences(false); + QVERIFY(!model->differenceAt(0)->applied()); + QCOMPARE(model->differenceAt(0)->sourceLineNumber(), 2); + QCOMPARE(model->differenceAt(0)->trackingDestinationLineNumber(), 2); + QVERIFY(!model->differenceAt(1)->applied()); + QCOMPARE(model->differenceAt(1)->sourceLineNumber(), 12); + QCOMPARE(model->differenceAt(1)->trackingDestinationLineNumber(), 12); + QVERIFY(!model->differenceAt(2)->applied()); + QCOMPARE(model->differenceAt(2)->sourceLineNumber(), 22); + QCOMPARE(model->differenceAt(2)->trackingDestinationLineNumber(), 22); + QVERIFY(!model->differenceAt(3)->applied()); + QCOMPARE(model->differenceAt(3)->sourceLineNumber(), 32); + QCOMPARE(model->differenceAt(3)->trackingDestinationLineNumber(), 32); + + model->differenceAt(1)->apply(true); + QVERIFY(model->differenceAt(1)->applied()); + QCOMPARE(model->differenceAt(1)->sourceLineNumber(), 12); + QCOMPARE(model->differenceAt(1)->trackingDestinationLineNumber(), 12); + QVERIFY(!model->differenceAt(2)->applied()); + QCOMPARE(model->differenceAt(2)->sourceLineNumber(), 22); + QCOMPARE(model->differenceAt(2)->trackingDestinationLineNumber(), 23); + QVERIFY(!model->differenceAt(3)->applied()); + QCOMPARE(model->differenceAt(3)->sourceLineNumber(), 32); + QCOMPARE(model->differenceAt(3)->trackingDestinationLineNumber(), 33); + + model->differenceAt(1)->apply(true); + QVERIFY(model->differenceAt(1)->applied()); + QCOMPARE(model->differenceAt(1)->sourceLineNumber(), 12); + QCOMPARE(model->differenceAt(1)->trackingDestinationLineNumber(), 12); + QVERIFY(!model->differenceAt(2)->applied()); + QCOMPARE(model->differenceAt(2)->sourceLineNumber(), 22); + QCOMPARE(model->differenceAt(2)->trackingDestinationLineNumber(), 23); + QVERIFY(!model->differenceAt(3)->applied()); + QCOMPARE(model->differenceAt(3)->sourceLineNumber(), 32); + QCOMPARE(model->differenceAt(3)->trackingDestinationLineNumber(), 33); + + model->differenceAt(2)->apply(true); + QVERIFY(model->differenceAt(2)->applied()); + QCOMPARE(model->differenceAt(2)->sourceLineNumber(), 22); + QCOMPARE(model->differenceAt(2)->trackingDestinationLineNumber(), 23); + QVERIFY(!model->differenceAt(3)->applied()); + QCOMPARE(model->differenceAt(3)->sourceLineNumber(), 32); + QCOMPARE(model->differenceAt(3)->trackingDestinationLineNumber(), 31); + + model->applyAllDifferences(true); + QVERIFY(model->differenceAt(0)->applied()); + QCOMPARE(model->differenceAt(0)->sourceLineNumber(), 2); + QCOMPARE(model->differenceAt(0)->trackingDestinationLineNumber(), 2); + QVERIFY(model->differenceAt(1)->applied()); + QCOMPARE(model->differenceAt(1)->sourceLineNumber(), 12); + QCOMPARE(model->differenceAt(1)->trackingDestinationLineNumber(), 13); + QVERIFY(model->differenceAt(2)->applied()); + QCOMPARE(model->differenceAt(2)->sourceLineNumber(), 22); + QCOMPARE(model->differenceAt(2)->trackingDestinationLineNumber(), 24); + QVERIFY(model->differenceAt(3)->applied()); + QCOMPARE(model->differenceAt(3)->sourceLineNumber(), 32); + QCOMPARE(model->differenceAt(3)->trackingDestinationLineNumber(), 32); + + model->applyAllDifferences(true); + QVERIFY(model->differenceAt(0)->applied()); + QCOMPARE(model->differenceAt(0)->sourceLineNumber(), 2); + QCOMPARE(model->differenceAt(0)->trackingDestinationLineNumber(), 2); + QVERIFY(model->differenceAt(1)->applied()); + QCOMPARE(model->differenceAt(1)->sourceLineNumber(), 12); + QCOMPARE(model->differenceAt(1)->trackingDestinationLineNumber(), 13); + QVERIFY(model->differenceAt(2)->applied()); + QCOMPARE(model->differenceAt(2)->sourceLineNumber(), 22); + QCOMPARE(model->differenceAt(2)->trackingDestinationLineNumber(), 24); + QVERIFY(model->differenceAt(3)->applied()); + QCOMPARE(model->differenceAt(3)->sourceLineNumber(), 32); + QCOMPARE(model->differenceAt(3)->trackingDestinationLineNumber(), 32); +} + +static void +contextDiff1() +{ + QStringList patch; + patch << + "commit 7377fcc682e85ef9784adb2a2da2c8c6756f9018 (HEAD, KDE/4.11)\n" << + "Author: Dr. Chocholoušek \n" << + "AuthorDate: Sat Jan 25 17:30:01 2014 +0100\n" << + "\n" << + " Fake diff.\n" << + "\n" << + "diff --git a/libdiff2/diffmodel.cpp b/libdiff2/diffmodel.cpp\n" << + "new file mode 100644\n" << + "index a42e82d..a8da0c9\n" << + "*** a/libdiff2/diffmodel.cpp\n" << // note the missing timestamps + "--- b/libdiff2/diffmodel.cpp\n" << + "*************** DiffModel::DiffModel() :\n" << + "*** 58,64 ****\n" << + " m_sourceFile( "" ),\n" << + " m_destinationFile( "" ),\n" << + " m_sourceTimestamp( "" ),\n" << + "! m_destinationTimestamp( "" ),\n" << + " m_sourceRevision( "" ),\n" << + " m_destinationRevision( "" ),\n" << + " m_appliedCount( 0 ),\n" << + "--- 58,64 ----\n" << + " m_sourceFile( "" ),\n" << + " m_destinationFile( "" ),\n" << + " m_sourceTimestamp( "" ),\n" << + "! m_destinationTimestamp( \"doh\" ),\n" << + " m_sourceRevision( "" ),\n" << + " m_destinationRevision( "" ),\n" << + " m_appliedCount( 0 ),\n" << + "*************** void DiffModel::splitSourceInPathAndFile\n" << + "*** 84,89 ****\n" << + "--- 84,91 ----\n" << + " if( ( pos = m_source.lastIndexOf( \"/\" ) ) >= 0 )\n" << + " m_sourcePath = m_source.mid( 0, pos+1 );\n" << + " \n" << + "+ add_this;\n" << + "+ \n" << + " if( ( pos = m_source.lastIndexOf( \"/\" ) ) >= 0 )\n" << + " m_sourceFile = m_source.mid( pos+1, m_source.length() - pos );\n" << + " else\n"; + Parser parser(0); + DiffModelList* models = parser.parse(patch); + QCOMPARE(models->size(), 1); + DiffModel* model = models->at(0); + QCOMPARE(model->differenceCount(), 2); + model->applyAllDifferences(true); + QVERIFY(model->differenceAt(0)->applied()); + QCOMPARE(model->differenceAt(0)->sourceLineNumber(), 61); + QCOMPARE(model->differenceAt(0)->trackingDestinationLineNumber(), 61); + QCOMPARE(model->differenceAt(1)->sourceLineNumber(), 87); + QCOMPARE(model->differenceAt(1)->trackingDestinationLineNumber(), 87); +} + +static void +contextDiff2() +{ + QStringList patch; + patch << + "*** a/libdiff2/diffmodel.cpp\n" << + "--- b/libdiff2/diffmodel.cpp\n" << + "***************\n" << + "*** 55,60 **** DiffModel::DiffModel() :\n" << // note the context here + "--- 55,61 ----\n" << + " m_destination( "" ),\n" << + " m_sourcePath( "" ),\n" << + " m_destinationPath( "" ),\n" << + "+ m_hoh ( "" );\n" << + " m_sourceFile( "" ),\n" << + " m_destinationFile( "" ),\n" << + " m_sourceTimestamp( "" ),\n"; + + Parser parser(0); + DiffModelList* models = parser.parse(patch); + QCOMPARE(models->size(), 1); + DiffModel* model = models->at(0); + QCOMPARE(model->differenceCount(), 1); + model->applyAllDifferences(true); + QVERIFY(model->differenceAt(0)->applied()); + QCOMPARE(model->differenceAt(0)->sourceLineNumber(), 58); + QCOMPARE(model->differenceAt(0)->trackingDestinationLineNumber(), 58); +} + +void InteractiveDiffTest::testContextDiff() +{ + contextDiff1(); + contextDiff2(); +} + +QTEST_MAIN(InteractiveDiffTest); diff --git a/libkomparediff2/tests/interactivedifftest.h b/libkomparediff2/tests/interactivedifftest.h new file mode 100644 index 00000000..d226c139 --- /dev/null +++ b/libkomparediff2/tests/interactivedifftest.h @@ -0,0 +1,48 @@ +/* + * This file is part of KDevelop + * Copyright 2011 Dmitry Risenberg + * + * 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 General Public + * 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 PATCHREVIEWTEST_H +#define PATCHREVIEWTEST_H + +#include + +#include "../difference.h" + +class InteractiveDiffTest : public QObject +{ + Q_OBJECT +private slots: + void testOneLineChange(); + void testSameLine(); + void testLineNumbers_data(); + void testLineNumbers(); + void testDifferenceContents_data(); + void testDifferenceContents(); + void testAppliedTouch(); + void testAppliedIntersect(); + void testExistingAndApplied(); + void testOneLineDeletionUnapplied(); + void testApplyUnapply(); + void testContextDiff(); +private: + void CompareDifferenceStringList(const Diff2::DifferenceStringList& actual, const QStringList& expected); +}; + +#endif // PATCHREVIEWTEST_H diff --git a/libkomparediff2/tests/levenshteintest.cpp b/libkomparediff2/tests/levenshteintest.cpp new file mode 100644 index 00000000..8029cf20 --- /dev/null +++ b/libkomparediff2/tests/levenshteintest.cpp @@ -0,0 +1,156 @@ +/* + * This file is part of KDevelop + * Copyright 2011 Dmitry Risenberg + * + * 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 General Public + * 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 "levenshteintest.h" + +#include +#include "../difference.h" +#include "../differencestringpair.h" +#include "../levenshteintable.h" +#include "../stringlistpair.h" + +using namespace Diff2; + +namespace QTest { + +template<> char* toString(const Marker& marker) +{ + QByteArray result = "Marker("; + if (marker.type() == Marker::Start) { + result += "Start, "; + } else { + result += "End, "; + } + result += QByteArray::number(marker.offset()); + result += ")"; + return qstrdup(result.data()); +} + +} + +void LevenshteinTest::testFirstEmptyString() +{ + DifferenceString* string1 = new DifferenceString(QString("12345")); + DifferenceString* string2 = new DifferenceString(QString("")); + DifferenceStringPair* pair = new DifferenceStringPair(string1, string2); + LevenshteinTable table; + table.createTable(pair); + table.createListsOfMarkers(); + MarkerList markersFirstExpected; + markersFirstExpected << new Marker(Marker::Start, 0) << new Marker(Marker::End, 5); + for (int i = 0; i < markersFirstExpected.size(); ++i) { + QCOMPARE(*string1->markerList()[i], *markersFirstExpected[i]); + } + MarkerList markersSecondExpected; + markersSecondExpected << new Marker(Marker::Start, 0) << new Marker(Marker::End, 0); + for (int i = 0; i < markersSecondExpected.size(); ++i) { + QCOMPARE(*string2->markerList()[i], *markersSecondExpected[i]); + } +} + +void LevenshteinTest::testSecondEmptyString() +{ + DifferenceString* string1 = new DifferenceString(QString("")); + DifferenceString* string2 = new DifferenceString(QString("12345")); + DifferenceStringPair* pair = new DifferenceStringPair(string1, string2); + LevenshteinTable table; + table.createTable(pair); + table.createListsOfMarkers(); + MarkerList markersFirstExpected; + markersFirstExpected << new Marker(Marker::Start, 0) << new Marker(Marker::End, 0); + for (int i = 0; i < markersFirstExpected.size(); ++i) { + QCOMPARE(*string1->markerList()[i], *markersFirstExpected[i]); + } + MarkerList markersSecondExpected; + markersSecondExpected << new Marker(Marker::Start, 0) << new Marker(Marker::End, 5); + for (int i = 0; i < markersSecondExpected.size(); ++i) { + QCOMPARE(*string2->markerList()[i], *markersSecondExpected[i]); + } +} + + +void LevenshteinTest::testDifferenceStrings() +{ + DifferenceString* string1 = new DifferenceString(QString("aaabcddefghik")); + DifferenceString* string2 = new DifferenceString(QString("aabcefghijk")); + DifferenceStringPair* pair = new DifferenceStringPair(string1, string2); + LevenshteinTable table; + table.createTable(pair); + table.createListsOfMarkers(); + MarkerList markersFirstExpected; + markersFirstExpected << new Marker(Marker::Start, 2) << new Marker(Marker::End, 3) << new Marker(Marker::Start, 5) << new Marker(Marker::End, 7); + for (int i = 0; i < markersFirstExpected.size(); ++i) { + QCOMPARE(*string1->markerList()[i], *markersFirstExpected[i]); + } + MarkerList markersSecondExpected; + markersSecondExpected << new Marker(Marker::Start, 9) << new Marker(Marker::End, 10); + for (int i = 0; i < markersSecondExpected.size(); ++i) { + QCOMPARE(*string2->markerList()[i], *markersSecondExpected[i]); + } +} + +void LevenshteinTest::testStringLists() +{ + QStringList list1; + list1 << "delete1" << "line1" << "line2" << "line3" << "delete2" << "delete3" << "line4"; + QStringList list2; + list2 << "line1" << "line2" << "line3" << "insert1" << "line4" <<"insert2"; + StringListPair* pair = new StringListPair(list1, list2); + LevenshteinTable table; + table.createTable(pair); + table.createListsOfMarkers(); + + MarkerList markersFirstExpected; + markersFirstExpected << new Marker(Marker::Start, 0) << new Marker(Marker::End, 1) << new Marker(Marker::Start, 4) << new Marker(Marker::End, 6); + for (int i = 0; i < markersFirstExpected.size(); ++i) { + QCOMPARE(*pair->markerListFirst()[i], *markersFirstExpected[i]); + } + MarkerList markersSecondExpected; + markersSecondExpected << new Marker(Marker::Start, 3) << new Marker(Marker::End, 4) << new Marker(Marker::Start, 5) << new Marker(Marker::End, 6); + for (int i = 0; i < markersSecondExpected.size(); ++i) { + QCOMPARE(*pair->markerListSecond()[i], *markersSecondExpected[i]); + } +} + +void LevenshteinTest::testSmth() +{ + QStringList list1; + list1 << "insert1\n" << "newline1\n" << "newline2\n" << "insert2\n";; + QStringList list2; + list2 << "delete1\n" << "efgh\n" << "delete2\n"; + StringListPair* pair = new StringListPair(list1, list2); + LevenshteinTable table; + table.createTable(pair); + table.createListsOfMarkers(); + + MarkerList markersFirstExpected; + markersFirstExpected << new Marker(Marker::Start, 0) << new Marker(Marker::End, 4); + for (int i = 0; i < markersFirstExpected.size(); ++i) { + QCOMPARE(*pair->markerListFirst()[i], *markersFirstExpected[i]); + } + MarkerList markersSecondExpected; + markersSecondExpected << new Marker(Marker::Start, 0) << new Marker(Marker::End, 3); + for (int i = 0; i < markersSecondExpected.size(); ++i) { + QCOMPARE(*pair->markerListSecond()[i], *markersSecondExpected[i]); + } +} + + +QTEST_MAIN(LevenshteinTest); diff --git a/libkomparediff2/tests/levenshteintest.h b/libkomparediff2/tests/levenshteintest.h new file mode 100644 index 00000000..ae05db47 --- /dev/null +++ b/libkomparediff2/tests/levenshteintest.h @@ -0,0 +1,37 @@ +/* + * This file is part of KDevelop + * Copyright 2011 Dmitry Risenberg + * + * 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 General Public + * 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 LEVENSHTEINTEST_H +#define LEVENSHTEINTEST_H + +#include + +class LevenshteinTest : public QObject +{ + Q_OBJECT +private slots: + void testFirstEmptyString(); + void testSecondEmptyString(); + void testDifferenceStrings(); + void testStringLists(); + void testSmth(); +}; + +#endif // LEVENSHTEINTEST_H