summaryrefslogtreecommitdiffstats
path: root/kompare
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commitbd9e6617827818fd043452c08c606f07b78014a0 (patch)
tree425bb4c3168f9c02f10150f235d2cb998dcc6108 /kompare
downloadtdesdk-bd9e6617827818fd043452c08c606f07b78014a0.tar.gz
tdesdk-bd9e6617827818fd043452c08c606f07b78014a0.zip
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdesdk@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kompare')
-rw-r--r--kompare/AUTHORS2
-rw-r--r--kompare/COPYING340
-rw-r--r--kompare/ChangeLog423
-rw-r--r--kompare/DESIGN35
-rw-r--r--kompare/Makefile.am43
-rw-r--r--kompare/README16
-rw-r--r--kompare/TODO34
-rw-r--r--kompare/interfaces/Makefile.am8
-rw-r--r--kompare/interfaces/kompareinterface.cpp60
-rw-r--r--kompare/interfaces/kompareinterface.h105
-rw-r--r--kompare/kompare.desktop74
-rw-r--r--kompare/kompare_shell.cpp487
-rw-r--r--kompare/kompare_shell.h149
-rw-r--r--kompare/komparenavigationpart.desktop4
-rw-r--r--kompare/komparenavtreepart/Makefile.am29
-rw-r--r--kompare/komparenavtreepart/komparenavtreepart.cpp710
-rw-r--r--kompare/komparenavtreepart/komparenavtreepart.desktop11
-rw-r--r--kompare/komparenavtreepart/komparenavtreepart.h191
-rw-r--r--kompare/komparepart/Makefile.am49
-rw-r--r--kompare/komparepart/kompare_part.cpp759
-rw-r--r--kompare/komparepart/kompare_part.h223
-rw-r--r--kompare/komparepart/kompare_qsplitter.h213
-rw-r--r--kompare/komparepart/kompareconnectwidget.cpp285
-rw-r--r--kompare/komparepart/kompareconnectwidget.h95
-rw-r--r--kompare/komparepart/komparelistview.cpp783
-rw-r--r--kompare/komparepart/komparelistview.h225
-rw-r--r--kompare/komparepart/komparepart.desktop24
-rw-r--r--kompare/komparepart/komparepartui.rc44
-rw-r--r--kompare/komparepart/kompareprefdlg.cpp106
-rw-r--r--kompare/komparepart/kompareprefdlg.h54
-rw-r--r--kompare/komparepart/komparesaveoptionsbase.ui336
-rw-r--r--kompare/komparepart/komparesaveoptionswidget.cpp215
-rw-r--r--kompare/komparepart/komparesaveoptionswidget.h51
-rw-r--r--kompare/komparepart/komparesplitter.cpp712
-rw-r--r--kompare/komparepart/komparesplitter.h115
-rw-r--r--kompare/kompareui.rc33
-rw-r--r--kompare/kompareurldialog.cpp143
-rw-r--r--kompare/kompareurldialog.h76
-rw-r--r--kompare/kompareviewpart.desktop4
-rw-r--r--kompare/libdialogpages/Makefile.am32
-rw-r--r--kompare/libdialogpages/diffpage.cpp355
-rw-r--r--kompare/libdialogpages/diffpage.h100
-rw-r--r--kompare/libdialogpages/diffsettings.cpp108
-rw-r--r--kompare/libdialogpages/diffsettings.h66
-rw-r--r--kompare/libdialogpages/filespage.cpp170
-rw-r--r--kompare/libdialogpages/filespage.h83
-rw-r--r--kompare/libdialogpages/filessettings.cpp60
-rw-r--r--kompare/libdialogpages/filessettings.h53
-rw-r--r--kompare/libdialogpages/pagebase.cpp104
-rw-r--r--kompare/libdialogpages/pagebase.h49
-rw-r--r--kompare/libdialogpages/settingsbase.cpp42
-rw-r--r--kompare/libdialogpages/settingsbase.h42
-rw-r--r--kompare/libdialogpages/viewpage.cpp179
-rw-r--r--kompare/libdialogpages/viewpage.h64
-rw-r--r--kompare/libdialogpages/viewsettings.cpp101
-rw-r--r--kompare/libdialogpages/viewsettings.h61
-rw-r--r--kompare/libdiff2/Makefile.am37
-rw-r--r--kompare/libdiff2/cvsdiffparser.cpp160
-rw-r--r--kompare/libdiff2/cvsdiffparser.h61
-rw-r--r--kompare/libdiff2/difference.cpp137
-rw-r--r--kompare/libdiff2/difference.h223
-rw-r--r--kompare/libdiff2/diffhunk.cpp115
-rw-r--r--kompare/libdiff2/diffhunk.h69
-rw-r--r--kompare/libdiff2/diffmodel.cpp409
-rw-r--r--kompare/libdiff2/diffmodel.h150
-rw-r--r--kompare/libdiff2/diffmodellist.cpp29
-rw-r--r--kompare/libdiff2/diffmodellist.h49
-rw-r--r--kompare/libdiff2/diffparser.cpp81
-rw-r--r--kompare/libdiff2/diffparser.h38
-rw-r--r--kompare/libdiff2/kompare.h144
-rw-r--r--kompare/libdiff2/komparemodellist.cpp1423
-rw-r--r--kompare/libdiff2/komparemodellist.h213
-rw-r--r--kompare/libdiff2/kompareprocess.cpp269
-rw-r--r--kompare/libdiff2/kompareprocess.h67
-rw-r--r--kompare/libdiff2/levenshteintable.cpp332
-rw-r--r--kompare/libdiff2/levenshteintable.h69
-rw-r--r--kompare/libdiff2/parser.cpp139
-rw-r--r--kompare/libdiff2/parser.h58
-rw-r--r--kompare/libdiff2/parserbase.cpp739
-rw-r--r--kompare/libdiff2/parserbase.h133
-rw-r--r--kompare/libdiff2/perforceparser.cpp223
-rw-r--r--kompare/libdiff2/perforceparser.h44
-rw-r--r--kompare/main.cpp217
-rw-r--r--kompare/pics/Makefile.am3
-rw-r--r--kompare/pics/hi128-app-kompare.pngbin0 -> 19472 bytes
-rw-r--r--kompare/pics/hi16-app-kompare.pngbin0 -> 917 bytes
-rw-r--r--kompare/pics/hi22-app-kompare.pngbin0 -> 1237 bytes
-rw-r--r--kompare/pics/hi32-app-kompare.pngbin0 -> 2771 bytes
-rw-r--r--kompare/pics/hi48-app-kompare.pngbin0 -> 4780 bytes
-rw-r--r--kompare/pics/hisc-app-kompare.svgzbin0 -> 1474 bytes
-rw-r--r--kompare/tests/cvsdiff/context.diff83
-rw-r--r--kompare/tests/cvsdiff/contextm.diff1046
-rw-r--r--kompare/tests/cvsdiff/ed.diff24
-rw-r--r--kompare/tests/cvsdiff/edm.diff692
-rw-r--r--kompare/tests/cvsdiff/normal.diff29
-rw-r--r--kompare/tests/cvsdiff/normalm.diff861
-rw-r--r--kompare/tests/cvsdiff/rcs.diff24
-rw-r--r--kompare/tests/cvsdiff/rcsm.diff683
-rw-r--r--kompare/tests/cvsdiff/unified.diff50
-rw-r--r--kompare/tests/cvsdiff/unifiedm.diff924
-rw-r--r--kompare/tests/diff/context.diff27
-rw-r--r--kompare/tests/diff/contextm.diff1032
-rw-r--r--kompare/tests/diff/contextp.diff27
-rw-r--r--kompare/tests/diff/ed.diff10
-rw-r--r--kompare/tests/diff/edm.diff680
-rw-r--r--kompare/tests/diff/normal.diff12
-rw-r--r--kompare/tests/diff/normalm.diff849
-rw-r--r--kompare/tests/diff/rcs.diff9
-rw-r--r--kompare/tests/diff/rcsm.diff671
-rw-r--r--kompare/tests/diff/unified.diff19
-rw-r--r--kompare/tests/diff/unifiedm.diff911
-rw-r--r--kompare/tests/diff/unifiedp.diff19
-rw-r--r--kompare/tests/perforce/context.diff8
-rw-r--r--kompare/tests/perforce/contextm.diff23
-rw-r--r--kompare/tests/perforce/rcs.diff3
-rw-r--r--kompare/tests/perforce/rcsm.diff12
-rw-r--r--kompare/tests/perforce/unified.diff6
-rw-r--r--kompare/tests/perforce/unifiedm.diff19
-rw-r--r--kompare/tests/subversion/context.diff9
-rw-r--r--kompare/tests/subversion/contextm.diff180
-rw-r--r--kompare/tests/subversion/ed.diff5
-rw-r--r--kompare/tests/subversion/edm.diff57
-rw-r--r--kompare/tests/subversion/normal.diff6
-rw-r--r--kompare/tests/subversion/normalm.diff170
-rw-r--r--kompare/tests/subversion/rcs.diff5
-rw-r--r--kompare/tests/subversion/rcsm.diff55
-rw-r--r--kompare/tests/subversion/unified.diff7
-rw-r--r--kompare/tests/subversion/unifiedm.diff173
128 files changed, 23986 insertions, 0 deletions
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 <otto.bruggeman@home.nl>
+John Firebaugh <jfirebaugh@kde.org>
diff --git a/kompare/COPYING b/kompare/COPYING
new file mode 100644
index 00000000..8832c184
--- /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.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 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.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+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.
+
+ <signature of Ty Coon>, 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/ChangeLog b/kompare/ChangeLog
new file mode 100644
index 00000000..d9db63b5
--- /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 KShellProcess and replaced it with a KProcess
+
+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/Makefile.am b/kompare/Makefile.am
new file mode 100644
index 00000000..bf5297b5
--- /dev/null
+++ b/kompare/Makefile.am
@@ -0,0 +1,43 @@
+SUBDIRS = interfaces libdiff2 libdialogpages komparenavtreepart komparepart pics
+
+INCLUDES = \
+ -I$(srcdir)/libdiff2 \
+ -I$(srcdir)/libdialogpages \
+ -I$(srcdir)/komparenavtreepart \
+ -I$(srcdir)/komparepart \
+ -I$(srcdir)/interfaces \
+ $(all_includes)
+
+noinst_HEADERS = kompare_shell.h kompareurldialog.h
+
+# let automoc handle all of the meta source files (moc)
+METASOURCES = AUTO
+
+messages: rc.cpp
+ $(EXTRACTRC) */*.rc */*.ui >> rc.cpp
+ $(XGETTEXT) `find . -name "*.cpp" -o -name "*.h"` -o $(podir)/kompare.pot
+
+#########################################################################
+# APPLICATION SECTION
+#########################################################################
+# this is the program that gets installed. it's name is used for all
+# of the other Makefile.am variables
+bin_PROGRAMS = kompare
+
+# the application source, library search path, and link libraries
+kompare_SOURCES = main.cpp kompare_shell.cpp kompareurldialog.cpp
+kompare_LDFLAGS = $(all_libraries)
+kompare_LDADD = $(LIB_KPARTS) \
+ $(top_builddir)/kompare/interfaces/libkompareinterface.la \
+ $(top_builddir)/kompare/libdialogpages/libdialogpages.la \
+ -lktexteditor
+
+# this is where the desktop file will go
+xdg_apps_DATA = kompare.desktop
+
+# this is where the shell's XML-GUI resource file goes
+shellrcdir = $(kde_datadir)/kompare
+shellrc_DATA = kompareui.rc
+
+kompareservicedir = $(kde_servicetypesdir)
+kompareservice_DATA = komparenavigationpart.desktop kompareviewpart.desktop
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/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/interfaces/Makefile.am b/kompare/interfaces/Makefile.am
new file mode 100644
index 00000000..ab8f9b93
--- /dev/null
+++ b/kompare/interfaces/Makefile.am
@@ -0,0 +1,8 @@
+INCLUDES = $(all_includes)
+
+lib_LTLIBRARIES = libkompareinterface.la
+
+libkompareinterface_la_LDFLAGS = -no-undefined $(all_libraries)
+libkompareinterface_la_SOURCES = kompareinterface.cpp
+libkompareinterface_la_LIBADD = $(LIB_QT)
+
diff --git a/kompare/interfaces/kompareinterface.cpp b/kompare/interfaces/kompareinterface.cpp
new file mode 100644
index 00000000..1522bad0
--- /dev/null
+++ b/kompare/interfaces/kompareinterface.cpp
@@ -0,0 +1,60 @@
+// Blah blah standard LGPL license
+// Copyright 2002-2004, Otto Bruggeman <otto.bruggeman@home.nl>
+
+#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..b9c0fcda
--- /dev/null
+++ b/kompare/interfaces/kompareinterface.h
@@ -0,0 +1,105 @@
+// blah blah standard LGPL license
+// Copyright 2002-2003, Otto Bruggeman <otto.bruggeman@home.nl>
+
+#ifndef _KOMPARE_INTERFACE_H
+#define _KOMPARE_INTERFACE_H
+
+#include <qstring.h>
+#include <qstringlist.h>
+#include <kdemacros.h>
+
+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 dont
+ * 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, 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;
+};
+
+#endif /* _KOMPARE_INTERFACE_H */
diff --git a/kompare/kompare.desktop b/kompare/kompare.desktop
new file mode 100644
index 00000000..88b7fcc4
--- /dev/null
+++ b/kompare/kompare.desktop
@@ -0,0 +1,74 @@
+# KDE Config File
+[Desktop Entry]
+Type=Application
+Name=Kompare
+Name[af]=K-vergelyk
+Name[eo]=Komparilo
+Name[hi]=काम्पेयर
+Name[lv]=Salīdzināt
+Name[ne]=तुलना
+Name[pa]=ਕੇ-ਤੁਲਨਾ
+Name[ta]=கோம்பெர்
+Name[th]=เปรียบเทียบ
+Name[tr]=Karşılaştır
+GenericName=Diff/Patch Frontend
+GenericName[af]=Diff/Lap Voorprogram
+GenericName[bs]=Frontend za Diff/Patch
+GenericName[ca]=Interfície per a 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[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[hi]=डिफ/पैच फ्रन्टएण्ड
+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[lt]=Diff/Patch naudotojo sąsaja
+GenericName[lv]=Diff/Patch Frontends
+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/Patch ਮੁੱਖ
+GenericName[pl]=Interfejs dla programów diff i patch
+GenericName[pt]=Interface do Diff/Patch
+GenericName[pt_BR]=Interface para Diferenças (diff)/Correções
+GenericName[ro]=Interfaţă grafică pentru "diff" şi "patch"
+GenericName[ru]=Сравнение файлов
+GenericName[sk]=Rozhranie Diff/Patch
+GenericName[sl]=Vmesnik za diff/patch
+GenericName[sr]=Интерфејс за Diff/Patch
+GenericName[sr@Latn]=Interfejs za Diff/Patch
+GenericName[sv]=Gränssnitt för Diff/Patch
+GenericName[ta]=டிப்/ஒட்டு முன்பகுதி
+GenericName[tg]=Утилитаи баробаркунии файлҳо
+GenericName[th]=ฟรอนต์เอนด์ของ Diff/Patch
+GenericName[tr]=Diff/Yama Önyüzü
+GenericName[uk]=Інтерфейс до diff/patch
+GenericName[zh_CN]=Diff/Patch 前端
+GenericName[zh_TW]=Diff/Patch 前端
+GenericName[zu]=Diff/PatchIsiqalo sokugcina
+MimeType=text/x-diff;
+Exec=kompare -caption "%c" %i %m -o %U
+Icon=kompare
+DocPath=kompare/index.html
+Terminal=false
+X-DCOP-ServiceType=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..3840e272
--- /dev/null
+++ b/kompare/kompare_shell.cpp
@@ -0,0 +1,487 @@
+/***************************************************************************
+ kompare_shell.cpp - description
+ -------------------
+ begin : Sun Mar 4 2001
+ copyright : (C) 2001-2004 Otto Bruggeman
+ (C) 2001-2003 John Firebaugh
+ email : otto.bruggeman@home.nl
+ jfirebaugh@kde.org
+****************************************************************************/
+
+/***************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+***************************************************************************/
+
+#include <ktexteditor/document.h>
+#include <ktexteditor/editinterface.h>
+#include <ktexteditor/view.h>
+#include <kdebug.h>
+#include <kedittoolbar.h>
+#include <kencodingfiledialog.h>
+#include <kiconloader.h>
+#include <kkeydialog.h>
+#include <klibloader.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kparts/componentfactory.h>
+#include <ksqueezedtextlabel.h>
+#include <kstatusbar.h>
+#include <kstdaction.h>
+#include <ktrader.h>
+#include <kuserprofile.h>
+
+#include "kompare_part.h"
+#include "komparenavtreepart.h"
+#include "kompareurldialog.h"
+
+#include "kompare_shell.h"
+
+#define ID_N_OF_N_DIFFERENCES 1
+#define ID_N_OF_N_FILES 2
+#define ID_GENERAL 3
+
+KompareShell::KompareShell()
+ : KParts::DockMainWindow( 0L, "KompareShell" ),
+ 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();
+
+ KTrader::OfferList offers = KTrader::self()->query( "text/x-diff",
+ "Kompare/ViewPart", QString::null, QString::null );
+#ifdef NDEBUG
+ for( int i = 0; i < offers.count(); i++ )
+ {
+ kdDebug(8102) << "One kservicetype checked..." << endl;
+ KService::Ptr ptr2 = *(offers.at( i ));
+ QStringList list = ptr2->serviceTypes();
+ for ( QStringList::Iterator it2 = list.begin(); it2 != list.end(); ++it2 )
+ kdDebug(8102) << *it2 << endl;
+ }
+#endif
+ if ( offers.count() == 0 )
+ {
+ KMessageBox::error(this, i18n( "Could not find our KompareViewPart." ) );
+ exit(1);
+ }
+
+ KService::Ptr ptr = offers.first();
+
+ KLibFactory *mainViewFactory = KLibLoader::self()->factory( ptr->library().ascii() );
+ if (mainViewFactory)
+ {
+ m_mainViewDock = createDockWidget( "View", kapp->icon() );
+ // now that the Part is loaded, we cast it to a KomparePart to get
+ // our hands on it
+ m_viewPart = static_cast<KomparePart*>(mainViewFactory->create(m_mainViewDock,
+ "kompare_part", "KParts::ReadWritePart" ));
+
+ if ( m_viewPart )
+ {
+ m_mainViewDock->setWidget( m_viewPart->widget() );
+ setView( m_mainViewDock );
+ setMainDockWidget( m_mainViewDock );
+
+ // 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);
+ }
+
+ offers.clear();
+ offers = KTrader::self()->query( "text/x-diff", "KParts/ReadOnlyPart", "'Kompare/NavigationPart' in ServiceTypes", QString::null );
+ if ( offers.count() == 0 )
+ {
+ KMessageBox::error(this, i18n( "Could not find our KompareNavigationPart." ) );
+ exit(3);
+ }
+
+ ptr = offers.first();
+
+ KLibFactory *navTreeFactory = KLibLoader::self()->factory( ptr->library().ascii() );
+ if (navTreeFactory)
+ {
+ m_navTreeDock = createDockWidget( "Navigation", kapp->icon() );
+
+ m_navTreePart = static_cast<KompareNavTreePart*>(navTreeFactory->create(m_navTreeDock,
+ "komparenavtreepart", "KParts::ReadOnlyPart" ));
+
+ if ( m_navTreePart )
+ {
+ m_navTreeDock->setWidget( m_navTreePart->widget() );
+ 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&) ) );
+
+ // Read basic main-view settings, and set to autosave
+ setAutoSaveSettings( "General Options" );
+}
+
+KompareShell::~KompareShell()
+{
+}
+
+bool KompareShell::queryClose()
+{
+ return m_viewPart->queryClose();
+}
+
+void KompareShell::openDiff(const KURL& url)
+{
+ kdDebug(8102) << "Url = " << url.prettyURL() << endl;
+ m_diffURL = url;
+ m_viewPart->openDiff( url );
+}
+
+void KompareShell::openStdin()
+{
+ kdDebug(8102) << "Using stdin to read the diff" << endl;
+ QFile file;
+ file.open( IO_ReadOnly, stdin );
+ QTextStream stream( &file );
+
+ QString diff = stream.read();
+
+ file.close();
+
+ m_viewPart->openDiff( diff );
+
+}
+
+void KompareShell::compare(const KURL& source,const KURL& destination )
+{
+ m_sourceURL = source;
+ m_destinationURL = destination;
+
+ m_viewPart->compare( source, destination );
+}
+
+void KompareShell::blend( const KURL& url1, const KURL& diff )
+{
+ m_sourceURL = url1;
+ m_destinationURL = diff;
+
+ m_viewPart->openDirAndDiff( url1, diff );
+}
+
+void KompareShell::setupActions()
+{
+ KAction* open = KStdAction::open(this, SLOT(slotFileOpen()), actionCollection());
+ open->setText( i18n( "&Open Diff..." ) );
+ new KAction( i18n("&Compare Files..."), "fileopen", Qt::CTRL + Qt::Key_C,
+ this, SLOT(slotFileCompareFiles()),
+ actionCollection(), "file_compare_files" );
+ new KAction( i18n("&Blend URL with Diff..."), "fileblend", Qt::CTRL + Qt::Key_B,
+ this, SLOT(slotFileBlendURLAndDiff()),
+ actionCollection(), "file_blend_url" );
+ KStdAction::quit( this, SLOT( slotFileClose() ), actionCollection() );
+
+#if KDE_VERSION >= KDE_MAKE_VERSION(3,1,90)
+ createStandardStatusBarAction();
+#endif
+ setStandardToolBarMenuEnabled(true);
+ m_showTextView = new KToggleAction( i18n("Show T&ext View"), 0, this, SLOT(slotShowTextView()),
+ actionCollection(), "options_show_text_view" );
+ m_showTextView->setCheckedState(i18n("Hide T&ext View"));
+
+ KStdAction::keyBindings(this, SLOT(optionsConfigureKeys()), actionCollection());
+ KStdAction::configureToolbars(this, SLOT(optionsConfigureToolbars()), actionCollection());
+}
+
+void KompareShell::setupStatusBar()
+{
+ // Made these entries permanent so they will appear on the right side
+ statusBar()->insertItem( i18n(" 0 of 0 differences "), ID_N_OF_N_DIFFERENCES, 0, true );
+ statusBar()->insertItem( i18n(" 0 of 0 files "), ID_N_OF_N_FILES, 0, true );
+
+ m_generalLabel = new KSqueezedTextLabel( "", 0, "general_statusbar_label" );
+ statusBar()->addWidget( m_generalLabel, 1, false );
+ m_generalLabel->setAlignment( Qt::AlignLeft );
+}
+
+void KompareShell::slotUpdateStatusBar( int modelIndex, int differenceIndex, int modelCount, int differenceCount, int appliedCount )
+{
+ kdDebug(8102) << "KompareShell::updateStatusBar()" << endl;
+
+ QString fileStr;
+ QString diffStr;
+
+ if ( modelIndex >= 0 )
+ fileStr = i18n( " %1 of %n file ", " %1 of %n files ", modelCount ).arg( modelIndex + 1 );
+ else
+ fileStr = i18n( " %n file ", " %n files ", modelCount );
+
+ if ( differenceIndex >= 0 )
+ diffStr = i18n( " %1 of %n difference, %2 applied ", " %1 of %n differences, %2 applied ", differenceCount )
+ .arg( differenceIndex + 1 ).arg( appliedCount );
+ else
+ diffStr = i18n( " %n difference ", " %n 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::setCaption( const QString& caption )
+{
+// kdDebug() << kdBacktrace();
+ KParts::DockMainWindow::setCaption( caption, m_viewPart->isModified() );
+}
+
+void KompareShell::saveProperties(KConfig* 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() );
+ }
+
+ m_viewPart->saveProperties( config );
+}
+
+void KompareShell::readProperties(KConfig* 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", "" );
+
+ m_viewPart->readProperties( config );
+
+ m_viewPart->compareFiles( m_sourceURL, m_destinationURL );
+ }
+ else if ( mode == "ShowingDiff" )
+ {
+ m_mode = Kompare::ShowingDiff;
+ m_diffURL = config->readPathEntry( "DiffUrl", "" );
+
+ m_viewPart->readProperties( config );
+
+ m_viewPart->openURL( m_diffURL );
+ }
+ else
+ { // just in case something weird has happened, dont 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 :)
+ m_viewPart->readProperties( config );
+ }
+}
+
+void KompareShell::slotFileOpen()
+{
+ // FIXME: use different filedialog which gets encoding
+ KURL url = KFileDialog::getOpenURL( QString::null, "text/x-diff", this );
+ if( !url.isEmpty() ) {
+ KompareShell* shell = new KompareShell();
+ kapp->ref();
+ shell->show();
+ shell->openDiff( url );
+ }
+}
+
+void KompareShell::slotFileBlendURLAndDiff()
+{
+ KompareURLDialog* dialog = new KompareURLDialog( 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::null, 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->setButtonOK( 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();
+ KompareShell* shell = new KompareShell();
+ kapp->ref();
+ shell->show();
+ shell->m_viewPart->setEncoding( dialog->encoding() );
+ shell->blend( m_sourceURL, m_destinationURL );
+ }
+ delete dialog;
+}
+
+void KompareShell::slotFileCompareFiles()
+{
+ KompareURLDialog* dialog = new KompareURLDialog( this );
+
+ dialog->setCaption( i18n( "Compare Files or Folders" ) );
+ dialog->setFirstGroupBoxTitle( i18n( "Source" ) );
+ dialog->setSecondGroupBoxTitle( i18n( "Destination" ) );
+
+ KGuiItem compareGuiItem( i18n( "Compare" ), QString::null, 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->setButtonOK( 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();
+ kapp->ref();
+ shell->show();
+ kdDebug() << "Damn it, i should be called !!! Oh and encoding is: " << dialog->encoding() << endl;
+ shell->m_viewPart->setEncoding( dialog->encoding() );
+ shell->compare( m_sourceURL, m_destinationURL );
+ }
+ delete dialog;
+}
+
+void KompareShell::slotFileClose()
+{
+ if ( m_viewPart->queryClose() )
+ {
+ delete this;
+ kapp->deref();
+ }
+}
+
+void KompareShell::slotShowTextView()
+{
+ if ( !m_textViewWidget )
+ {
+ int errCode;
+
+ // FIXME: proper error checking
+ m_textViewWidget = createDockWidget( i18n("Text View"), SmallIcon( "text") );
+ m_textViewPart = KParts::ComponentFactory::createPartInstanceFromQuery<KTextEditor::Document>(
+ QString::fromLatin1("KTextEditor/Document"),
+ QString::null, (QWidget*)this, 0, (QWidget*)this, 0, QStringList(), &errCode );
+ if ( m_textViewPart )
+ {
+ m_textView = m_textViewPart->createView( this, 0 );
+ m_textViewWidget->setWidget( static_cast<QWidget*>(m_textView) );
+ m_textEditIface = editInterface( m_textViewPart );
+ connect( m_viewPart, SIGNAL(diffString(const QString&)),
+ this, SLOT(slotSetDiffString(const QString&)) );
+ }
+ }
+
+ m_textViewWidget->manualDock( m_mainViewDock, KDockWidget:: DockCenter );
+}
+
+void KompareShell::slotSetDiffString( const QString& diffString )
+{
+ if ( m_textEditIface )
+ m_textEditIface->setText( diffString );
+}
+
+void KompareShell::optionsConfigureKeys()
+{
+ KKeyDialog dlg( true, this );
+
+ dlg.insert( actionCollection() );
+ if ( m_viewPart )
+ dlg.insert( m_viewPart->actionCollection() );
+
+ dlg.configure();
+}
+
+void KompareShell::optionsConfigureToolbars()
+{
+ saveMainWindowSettings( KGlobal::config(), autoSaveGroup() );
+ // use the standard toolbar editor
+ KEditToolbar dlg(factory());
+ connect(&dlg,SIGNAL(newToolbarConfig()),this,SLOT(newToolbarConfig()));
+ dlg.exec();
+}
+
+void KompareShell::newToolbarConfig()
+{
+ applyMainWindowSettings( KGlobal::config(), autoSaveGroup() );
+}
+
+#include "kompare_shell.moc"
diff --git a/kompare/kompare_shell.h b/kompare/kompare_shell.h
new file mode 100644
index 00000000..be3bb150
--- /dev/null
+++ b/kompare/kompare_shell.h
@@ -0,0 +1,149 @@
+/***************************************************************************
+ kompare_shell.h - description
+ -------------------
+ begin : Sun Mar 4 2001
+ copyright : (C) 2001-2003 by Otto Bruggeman
+ and John Firebaugh
+ email : otto.bruggeman@home.nl
+ jfirebaugh@kde.org
+****************************************************************************/
+
+/***************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+***************************************************************************/
+
+#ifndef KOMPARESHELL_H
+#define KOMPARESHELL_H
+
+#include <kapplication.h>
+#include <kparts/dockmainwindow.h>
+
+#include "kompare.h"
+
+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 seperate view and navigation parts
+*
+* @short Application Shell
+* @author John Firebaugh <jfirebaugh@kde.org>
+* @author Otto Bruggeman <bruggie@home.nl>
+* @version 3.2.90
+*/
+class KompareShell : public KParts::DockMainWindow
+{
+ 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 );
+ void setCaption( const QString& caption );
+
+ /**
+ * This method only exists because i cant make main a frined of this class
+ */
+ KomparePart* viewPart() const { return m_viewPart; }
+
+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(KConfig *);
+
+ /**
+ * 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(KConfig *);
+
+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();
+
+private:
+ void setupAccel();
+ void setupActions();
+ void setupStatusBar();
+
+private:
+ KURL m_sourceURL;
+ KURL m_destinationURL;
+ KURL m_diffURL;
+
+ KomparePart* m_viewPart;
+ KompareNavTreePart* m_navTreePart;
+ KTextEditor::Document* m_textViewPart;
+ KTextEditor::View* m_textView;
+ KTextEditor::EditInterface* m_textEditIface;
+
+ KDockWidget* m_textViewWidget;
+ KDockWidget* m_mainViewDock;
+ KDockWidget* m_navTreeDock;
+
+ KToggleAction* m_showTextView;
+
+ enum Kompare::Mode m_mode;
+ // This is the statusbarwidget for displaying the general stuff
+ KSqueezedTextLabel* m_generalLabel;
+};
+
+#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/Makefile.am b/kompare/komparenavtreepart/Makefile.am
new file mode 100644
index 00000000..cac18964
--- /dev/null
+++ b/kompare/komparenavtreepart/Makefile.am
@@ -0,0 +1,29 @@
+#########################################################################
+# KPART SECTION
+#########################################################################
+
+INCLUDES = \
+ -I$(top_srcdir)/kompare/libdiff2 \
+ -I$(top_srcdir)/kompare/komparepart \
+ $(all_includes)
+
+noinst_HEADERS = \
+ komparenavtreepart.h
+
+# let automoc handle all of the meta source files (moc)
+METASOURCES = AUTO
+
+kde_module_LTLIBRARIES = libkomparenavtreepart.la
+
+# the Part's source, library search path, and link libraries
+libkomparenavtreepart_la_SOURCES = \
+ komparenavtreepart.cpp
+
+libkomparenavtreepart_la_LDFLAGS = $(KDE_PLUGIN) $(all_libraries)
+libkomparenavtreepart_la_LIBADD = $(LIB_KPARTS) ../libdiff2/libdiff2.la
+
+# this is where the desktop file will go
+partdesktopdir = $(kde_servicesdir)
+partdesktop_DATA = komparenavtreepart.desktop
+
+
diff --git a/kompare/komparenavtreepart/komparenavtreepart.cpp b/kompare/komparenavtreepart/komparenavtreepart.cpp
new file mode 100644
index 00000000..f2e10759
--- /dev/null
+++ b/kompare/komparenavtreepart/komparenavtreepart.cpp
@@ -0,0 +1,710 @@
+/***************************************************************************
+ KompareNavTreePart.cpp - description
+ -------------------
+ begin : Sun Mar 4 2001
+ copyright : (C) 2001-2004 Otto Bruggeman
+ (C) 2001-2003 John Firebaugh
+ email : otto.bruggeman@home.nl
+ jfirebaugh@kde.org
+****************************************************************************/
+
+/***************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+***************************************************************************/
+
+#include <kdebug.h>
+#include <klocale.h>
+#include <kiconloader.h>
+#include <kmimetype.h>
+#include <klistview.h>
+#include <kaboutdata.h>
+
+#include "difference.h"
+#include "diffmodel.h"
+#include "diffmodellist.h"
+#include "komparemodellist.h"
+
+#include "komparenavtreepart.h"
+
+#define COL_SOURCE 0
+#define COL_DESTINATION 1
+#define COL_DIFFERENCE 2
+
+using namespace Diff2;
+
+KompareNavTreePart::KompareNavTreePart( QWidget* parent, const char* name )
+ : KParts::ReadOnlyPart( parent, name ),
+ 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 );
+
+ setWidget( m_splitter );
+
+ m_srcDirTree = new KListView( m_splitter );
+ m_srcDirTree->addColumn( i18n("Source Folder") );
+ m_srcDirTree->setRootIsDecorated( false );
+ m_srcDirTree->setSorting( 0, true );
+
+ m_destDirTree = new KListView( m_splitter );
+ m_destDirTree->addColumn( i18n("Destination Folder") );
+ m_destDirTree->setRootIsDecorated( false );
+ m_destDirTree->setSorting( 0, true );
+
+ m_fileList = new KListView( m_splitter );
+ m_fileList->addColumn( i18n("Source File") );
+ m_fileList->addColumn( i18n("Destination File") );
+ m_fileList->setAllColumnsShowFocus( true );
+ m_fileList->setRootIsDecorated( false );
+ m_fileList->setSorting( 0, true );
+
+ m_changesList = new KListView( m_splitter );
+ m_changesList->addColumn( i18n("Source Line") );
+ m_changesList->addColumn( i18n("Destination Line") );
+ m_changesList->addColumn( i18n("Difference") );
+ m_changesList->setAllColumnsShowFocus( true );
+ m_changesList->setRootIsDecorated( false );
+ m_changesList->setSorting( 0, true );
+
+ connect( m_srcDirTree, SIGNAL(selectionChanged( QListViewItem* )),
+ this, SLOT(slotSrcDirTreeSelectionChanged( QListViewItem* )) );
+ connect( m_destDirTree, SIGNAL(selectionChanged( QListViewItem* )),
+ this, SLOT(slotDestDirTreeSelectionChanged( QListViewItem* )) );
+ connect( m_fileList, SIGNAL(selectionChanged( QListViewItem* )),
+ this, SLOT(slotFileListSelectionChanged( QListViewItem* )) );
+ connect( m_changesList, SIGNAL(selectionChanged( QListViewItem* )),
+ this, SLOT(slotChangesListSelectionChanged( QListViewItem* )) );
+}
+
+KompareNavTreePart::~KompareNavTreePart()
+{
+}
+
+void KompareNavTreePart::slotKompareInfo( struct Kompare::Info* info )
+{
+ m_info = info;
+}
+
+void KompareNavTreePart::slotModelsChanged( const DiffModelList* modelList )
+{
+ kdDebug(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()
+{
+ kdDebug(8105) << "BuildTreeInMemory called" << endl;
+
+ if ( m_modelList->count() == 0 )
+ {
+ kdDebug() << "No models... weird shit..." << endl;
+ return; // avoids a crash on clear()
+ }
+
+ if ( m_info == 0 )
+ {
+ kdDebug() << "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:
+ srcBase = model->sourcePath();
+ destBase = model->destinationPath();
+ 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:
+ kdDebug(8105) << "Oops needs to implement this..." << endl;
+ }
+
+// kdDebug(8105) << "srcBase = " << srcBase << endl;
+// kdDebug(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();
+
+ kdDebug(8105) << "srcPath = " << srcPath << endl;
+ kdDebug(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
+// kdDebug(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 )
+{
+ kdDebug(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[ (void*)model ];
+ kdDebug(8105) << "Manually setting selection in srcdirtree with currentDir = " << currentDir << endl;
+ m_srcDirTree->blockSignals( true );
+ m_srcDirTree->setSelected( currentDir, true );
+ m_srcDirTree->ensureItemVisible( currentDir );
+ m_srcDirTree->blockSignals( false );
+
+ currentDir = m_modelToDestDirItemDict[ (void*)model ];
+ kdDebug(8105) << "Manually setting selection in destdirtree with currentDir = " << currentDir << endl;
+ m_destDirTree->blockSignals( true );
+ m_destDirTree->setSelected( currentDir, true );
+ m_destDirTree->ensureItemVisible( 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[ (void*)model ];
+ kdDebug(8105) << "Manually setting selection in filelist" << endl;
+ m_fileList->blockSignals( true );
+ m_fileList->setSelected( currentFile, true );
+ m_fileList->ensureItemVisible( 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[ (void*)diff ];
+ kdDebug(8105) << "Manually setting selection in changeslist to " << currentDiff << endl;
+ m_changesList->blockSignals( true );
+ m_changesList->setSelected( currentDiff, true );
+ m_changesList->ensureItemVisible( currentDiff );
+ m_changesList->blockSignals( false );
+}
+
+void KompareNavTreePart::slotSetSelection( const Difference* diff )
+{
+// kdDebug(8105) << "Scotty i need more power !!" << endl;
+ if ( m_selectedDifference != diff )
+ {
+// kdDebug(8105) << "But sir, i am giving you all she's got" << endl;
+ m_selectedDifference = diff;
+ setSelectedDifference( diff );
+ }
+}
+
+void KompareNavTreePart::slotSrcDirTreeSelectionChanged( QListViewItem* item )
+{
+ kdDebug(8105) << "Sent by the sourceDirectoryTree with item = " << item << endl;
+ m_srcDirTree->ensureItemVisible( item );
+ KDirLVI* dir = static_cast<KDirLVI*>(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->setSelected( selItem, true );
+ m_destDirTree->ensureItemVisible( selItem );
+ m_destDirTree->blockSignals( false );
+ // fill the changes list
+ dir->fillFileList( m_fileList, &m_modelToFileItemDict );
+}
+
+void KompareNavTreePart::slotDestDirTreeSelectionChanged( QListViewItem* item )
+{
+ kdDebug(8105) << "Sent by the destinationDirectoryTree with item = " << item << endl;
+ m_destDirTree->ensureItemVisible( item );
+ KDirLVI* dir = static_cast<KDirLVI*>(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->setSelected( selItem, true );
+ m_srcDirTree->ensureItemVisible( selItem );
+ m_srcDirTree->blockSignals( false );
+ // fill the changes list
+ dir->fillFileList( m_fileList, &m_modelToFileItemDict );
+}
+
+void KompareNavTreePart::slotFileListSelectionChanged( QListViewItem* item )
+{
+ kdDebug(8105) << "Sent by the fileList with item = " << item << endl;
+
+ KFileLVI* file = static_cast<KFileLVI*>(item);
+ m_selectedModel = file->model();
+ m_changesList->blockSignals( true );
+ file->fillChangesList( m_changesList, &m_diffToChangeItemDict );
+ m_changesList->blockSignals( false );
+
+ if ( m_changesList->selectedItem() )
+ {
+ // FIXME: This is ugly...
+ m_selectedDifference = (static_cast<KChangeLVI*>(m_changesList->selectedItem()))->difference();
+ }
+
+ emit selectionChanged( m_selectedModel, m_selectedDifference );
+}
+
+void KompareNavTreePart::slotChangesListSelectionChanged( QListViewItem* item )
+{
+ kdDebug(8105) << "Sent by the changesList" << endl;
+
+ KChangeLVI* change = static_cast<KChangeLVI*>(item);
+ m_selectedDifference = change->difference();
+
+ emit selectionChanged( m_selectedDifference );
+}
+
+void KompareNavTreePart::slotApplyDifference( bool /*apply*/ )
+{
+ KChangeLVI* clvi = m_diffToChangeItemDict[(void*)m_selectedDifference];
+ if ( clvi )
+ clvi->setDifferenceText();
+}
+
+void KompareNavTreePart::slotApplyAllDifferences( bool /*apply*/ )
+{
+ QPtrDictIterator<KChangeLVI> it( m_diffToChangeItemDict );
+
+ kdDebug() << "m_diffToChangeItemDict.count() = " << m_diffToChangeItemDict.count() << endl;
+
+ for ( ; it.current(); ++it )
+ {
+ it.current()->setDifferenceText();
+ }
+}
+
+void KompareNavTreePart::slotApplyDifference( const Difference* diff, bool /*apply*/ )
+{
+ // this applies to the currently selected difference
+ KChangeLVI* clvi = m_diffToChangeItemDict[(void*)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 = i18n( "Applied: Changes made to %n line undone", "Applied: Changes made to %n lines undone",
+ m_difference->sourceLineCount() );
+ else
+ text = i18n( "Changed %n line", "Changed %n lines",
+ m_difference->sourceLineCount() );
+ break;
+ case Difference::Insert:
+ if( m_difference->applied() )
+ text = i18n( "Applied: Insertion of %n line undone", "Applied: Insertion of %n lines undone",
+ m_difference->destinationLineCount() );
+ else
+ text = i18n( "Inserted %n line", "Inserted %n lines",
+ m_difference->destinationLineCount() );
+ break;
+ case Difference::Delete:
+ if( m_difference->applied() )
+ text = i18n( "Applied: Deletion of %n line undone", "Applied: Deletion of %n lines undone",
+ m_difference->sourceLineCount() );
+ else
+ text = i18n( "Deleted %n line", "Deleted %n lines",
+ m_difference->sourceLineCount() );
+ break;
+ default:
+ kdDebug(8105) << "Unknown or Unchanged enum value when checking for diff->type() in KChangeLVI's constructor" << endl;
+ text = "";
+ }
+
+ setText( 2, text );
+}
+
+KChangeLVI::KChangeLVI( KListView* parent, Difference* diff ) : KListViewItem( parent )
+{
+ m_difference = diff;
+
+ setText( 0, QString::number( diff->sourceLineNumber() ) );
+ setText( 1, QString::number( diff->destinationLineNumber() ) );
+
+ setDifferenceText();
+}
+
+int KChangeLVI::compare( QListViewItem* item, int column, bool ascending ) const
+{
+ if ( ascending )
+ {
+ if ( this->text(column).length() < item->text(column).length() )
+ return -1;
+ if ( this->text(column).length() > item->text(column).length() )
+ return 1;
+ }
+ else
+ {
+ if ( this->text(column).length() > item->text(column).length() )
+ return -1;
+ if ( this->text(column).length() < item->text(column).length() )
+ return 1;
+ }
+
+ return key( column, ascending ).compare( item->key( column, ascending ) );
+}
+
+KChangeLVI::~KChangeLVI()
+{
+}
+
+KFileLVI::KFileLVI( KListView* parent, DiffModel* model ) : KListViewItem( parent )
+{
+ m_model = model;
+
+ setText( 0, model->sourceFile() );
+ setText( 1, model->destinationFile() );
+ setPixmap( 0, SmallIcon( "txt" ) );
+ setPixmap( 1, SmallIcon( "txt" ) );
+ setSelectable( true );
+}
+
+void KFileLVI::fillChangesList( KListView* changesList, QPtrDict<KChangeLVI>* diffToChangeItemDict )
+{
+ changesList->clear();
+ diffToChangeItemDict->clear();
+
+ DifferenceListConstIterator diffIt = m_model->differences()->begin();
+ DifferenceListConstIterator dEnd = m_model->differences()->end();
+
+ for ( ; diffIt != dEnd; ++diffIt )
+ {
+ KChangeLVI* change = new KChangeLVI( changesList, *diffIt );
+ diffToChangeItemDict->insert( *diffIt, change );
+ }
+
+ changesList->setSelected( changesList->firstChild(), true );
+}
+
+KFileLVI::~KFileLVI()
+{
+}
+
+KDirLVI::KDirLVI( KListView* parent, QString& dir ) : KListViewItem( parent )
+{
+// kdDebug(8105) << "KDirLVI (KListView) constructor called with dir = " << dir << endl;
+ m_rootItem = true;
+ m_dirName = dir;
+ setPixmap( 0, SmallIcon( "folder" ) );
+ setOpen( true );
+ setSelectable( true );
+ if ( m_dirName.isEmpty() )
+ setText( 0, i18n( "Unknown" ) );
+ else
+ setText( 0, m_dirName );
+}
+
+KDirLVI::KDirLVI( KDirLVI* parent, QString& dir ) : KListViewItem( parent )
+{
+// kdDebug(8105) << "KDirLVI (KDirLVI) constructor called with dir = " << dir << endl;
+ m_rootItem = false;
+ m_dirName = dir;
+ setPixmap( 0, SmallIcon( "folder" ) );
+ setOpen( true );
+ setSelectable( true );
+ setText( 0, m_dirName );
+}
+
+// addModel always removes it own path from the beginning
+void KDirLVI::addModel( QString& path, DiffModel* model, QPtrDict<KDirLVI>* modelToDirItemDict )
+{
+// kdDebug(8105) << "KDirLVI::addModel called with path = " << path << " from KDirLVI with m_dirName = " << m_dirName << endl;
+
+ if ( !m_dirName.isEmpty() )
+ {
+ if ( path.find( m_dirName ) > -1 )
+ path = path.replace( path.find( m_dirName ), m_dirName.length(), "" );
+ }
+
+// kdDebug(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.find( "/", 0 ) + 1 );
+ child = findChild( dir );
+ if ( !child )
+ {
+ // does not exist yet so make it
+// kdDebug(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 )
+{
+// kdDebug(8105) << "KDirLVI::findChild called with dir = " << dir << endl;
+ KDirLVI* child;
+ if ( ( child = static_cast<KDirLVI*>(this->firstChild()) ) != 0L )
+ { // has children, check if dir already exists, if so addModel
+ do {
+ if ( dir == child->dirName() )
+ return child;
+ } while ( ( child = static_cast<KDirLVI*>(child->nextSibling()) ) != 0L );
+ }
+
+ return 0L;
+}
+
+void KDirLVI::fillFileList( KListView* fileList, QPtrDict<KFileLVI>* 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->setSelected( fileList->firstChild(), true );
+}
+
+QString KDirLVI::fullPath( QString& path )
+{
+// if ( !path.isEmpty() )
+// kdDebug(8105) << "KDirLVI::fullPath called with path = " << path << endl;
+// else
+// kdDebug(8105) << "KDirLVI::fullPath called with empty path..." << endl;
+
+ if ( m_rootItem ) // dont bother adding rootItem's dir...
+ return path;
+
+ path = path.prepend( m_dirName );
+
+ KDirLVI* lviParent = dynamic_cast<KDirLVI*>( parent() );
+ if ( lviParent )
+ {
+ path = lviParent->fullPath( path );
+ }
+
+ return path;
+}
+
+KDirLVI* KDirLVI::setSelected( QString dir )
+{
+// kdDebug(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<KDirLVI*>(firstChild());
+ if ( !child )
+ return 0L;
+
+ do {
+ if ( dir.startsWith( child->dirName() ) )
+ return child->setSelected( dir );
+ } while ( ( child = static_cast<KDirLVI*>(child->nextSibling()) ) != 0L );
+
+ return 0L;
+}
+
+KDirLVI::~KDirLVI()
+{
+}
+
+// part stuff
+KInstance* KompareNavTreePartFactory::s_instance = 0L;
+KAboutData* KompareNavTreePartFactory::s_about = 0L;
+
+KompareNavTreePartFactory::KompareNavTreePartFactory()
+ : KParts::Factory()
+{
+}
+
+KompareNavTreePartFactory::~KompareNavTreePartFactory()
+{
+ delete s_instance;
+ delete s_about;
+
+ s_instance = 0L;
+}
+
+KParts::Part* KompareNavTreePartFactory::createPartObject( QWidget* parentWidget, const char* widgetName,
+ QObject* /*parent*/, const char* /*name*/,
+ const char* /*classname*/, const QStringList & /*args*/ )
+{
+ // Create an instance of our Part
+ KompareNavTreePart* obj = new KompareNavTreePart( parentWidget, widgetName );
+
+ KGlobal::locale()->insertCatalogue("kompare");
+
+ return obj;
+}
+
+KInstance* KompareNavTreePartFactory::instance()
+{
+ if( !s_instance )
+ {
+ s_about = new KAboutData("komparenavtreepart", I18N_NOOP("KompareNavTreePart"), "1.1");
+ s_about->addAuthor("John Firebaugh", "Author", "jfirebaugh@kde.org");
+ s_about->addAuthor("Otto Bruggeman", "Author", "otto.bruggeman@home.nl" );
+ s_instance = new KInstance(s_about);
+ }
+ return s_instance;
+}
+
+extern "C"
+{
+ KDE_EXPORT void* init_libkomparenavtreepart()
+ {
+ return new KompareNavTreePartFactory;
+ }
+}
+
+#include "komparenavtreepart.moc"
diff --git a/kompare/komparenavtreepart/komparenavtreepart.desktop b/kompare/komparenavtreepart/komparenavtreepart.desktop
new file mode 100644
index 00000000..eb5ce686
--- /dev/null
+++ b/kompare/komparenavtreepart/komparenavtreepart.desktop
@@ -0,0 +1,11 @@
+[Desktop Entry]
+Name=KompareNavTreePart
+Name[pl]=Komponent drzewa nawigacyjnego Kompare
+Name[pt_BR]=Componente da árvore do Kompare
+Name[sv]=Kompare-navigeringsträdsdel
+Name[ta]=கோம்பெர் மரம் பாகம்
+MimeType=text/x-diff
+ServiceTypes=Kompare/NavigationPart
+X-KDE-Library=libkomparenavtreepart
+Type=Service
+Icon=kompare
diff --git a/kompare/komparenavtreepart/komparenavtreepart.h b/kompare/komparenavtreepart/komparenavtreepart.h
new file mode 100644
index 00000000..e2563d63
--- /dev/null
+++ b/kompare/komparenavtreepart/komparenavtreepart.h
@@ -0,0 +1,191 @@
+/***************************************************************************
+ komparenavtreepart.h - description
+ -------------------
+ begin : Mon Feb 26 2002
+ copyright : (C) 2001-2004 Otto Bruggeman
+ (C) 2001-2003 John Firebaugh
+ email : otto.bruggeman@home.nl
+ jfirebaugh@kde.org
+****************************************************************************/
+
+/***************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+***************************************************************************/
+
+#ifndef KOMPARENAVTREEPART_H
+#define KOMPARENAVTREEPART_H
+
+#include <qptrdict.h>
+#include <qptrlist.h>
+#include <qsplitter.h>
+#include <qlistview.h>
+
+#include <kparts/factory.h>
+#include <kparts/part.h>
+
+#include "kompare.h"
+#include "diffmodellist.h"
+
+class KompareModelList;
+class KomparePart;
+class KListView;
+
+namespace Diff2 {
+class DiffModel;
+class Difference;
+}
+
+class KDirLVI;
+class KFileLVI;
+class KChangeLVI;
+
+class KompareNavTreePart : public KParts::ReadOnlyPart
+{
+ Q_OBJECT
+
+public:
+ KompareNavTreePart( QWidget* parent = 0L, const char* name = 0L );
+ 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 ( QListViewItem* item );
+ void slotDestDirTreeSelectionChanged( QListViewItem* item );
+ void slotFileListSelectionChanged ( QListViewItem* item );
+ void slotChangesListSelectionChanged( QListViewItem* 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 );
+
+ KListViewItem* findDirInDirTree( const KListViewItem* parent, const QString& dir );
+
+// KListViewItem* firstItem();
+// KListViewItem* lastItem();
+
+private:
+ QSplitter* m_splitter;
+ const Diff2::DiffModelList* m_modelList;
+
+ QPtrDict<KChangeLVI> m_diffToChangeItemDict;
+ QPtrDict<KFileLVI> m_modelToFileItemDict;
+ QPtrDict<KDirLVI> m_modelToSrcDirItemDict;
+ QPtrDict<KDirLVI> m_modelToDestDirItemDict;
+
+ KListView* m_srcDirTree;
+ KListView* m_destDirTree;
+ KListView* m_fileList;
+ KListView* 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 KListViewItem
+{
+public:
+ KChangeLVI( KListView* parent, Diff2::Difference* diff );
+ ~KChangeLVI();
+public:
+ Diff2::Difference* difference() { return m_difference; };
+ virtual int compare( QListViewItem* item, int column, bool ascending ) const;
+
+ void setDifferenceText();
+private:
+ Diff2::Difference* m_difference;
+};
+
+class KFileLVI : public KListViewItem
+{
+public:
+ KFileLVI( KListView* parent, Diff2::DiffModel* model );
+ ~KFileLVI();
+public:
+ Diff2::DiffModel* model() { return m_model; };
+ void fillChangesList( KListView* changesList, QPtrDict<KChangeLVI>* diffToChangeItemDict );
+private:
+ Diff2::DiffModel* m_model;
+};
+
+class KDirLVI : public KListViewItem
+{
+public:
+ KDirLVI( KDirLVI* parent, QString& dir );
+ KDirLVI( KListView* parent, QString& dir );
+ ~KDirLVI();
+public:
+ void addModel( QString& dir, Diff2::DiffModel* model, QPtrDict<KDirLVI>* modelToDirItemDict );
+ QString& dirName() { return m_dirName; };
+ QString fullPath( QString& path );
+ KDirLVI* setSelected( QString dir );
+ void fillFileList( KListView* fileList, QPtrDict<KFileLVI>* modelToFileItemDict );
+ bool isRootItem() { return m_rootItem; };
+private:
+ KDirLVI* findChild( QString dir );
+private:
+ Diff2::DiffModelList m_modelList;
+ QString m_dirName;
+ bool m_rootItem;
+};
+
+// part stuff
+class KInstance;
+class KAboutData;
+
+class KompareNavTreePartFactory : public KParts::Factory
+{
+ Q_OBJECT
+public:
+ KompareNavTreePartFactory();
+ virtual ~KompareNavTreePartFactory();
+ virtual KParts::Part* createPartObject( QWidget *parentWidget, const char *widgetName,
+ QObject *parent, const char *name,
+ const char *classname, const QStringList &args );
+ static KInstance* instance();
+
+private:
+ static KInstance* s_instance;
+ static KAboutData* s_about;
+};
+
+#endif
diff --git a/kompare/komparepart/Makefile.am b/kompare/komparepart/Makefile.am
new file mode 100644
index 00000000..f076448b
--- /dev/null
+++ b/kompare/komparepart/Makefile.am
@@ -0,0 +1,49 @@
+#########################################################################
+# KPART SECTION
+#########################################################################
+
+INCLUDES = \
+ -I$(top_srcdir)/kompare/libdialogpages \
+ -I$(top_srcdir)/kompare/libdiff2 \
+ -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 \
+ ../libdiff2/libdiff2.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..d74945e6
--- /dev/null
+++ b/kompare/komparepart/kompare_part.cpp
@@ -0,0 +1,759 @@
+/***************************************************************************
+ kompare_part.cpp - description
+ -------------------
+ begin : Sun Mar 4 2001
+ copyright : (C) 2001-2004 Otto Bruggeman
+ (C) 2001-2003 John Firebaugh
+ (C) 2004 Jeff Snyder
+ email : otto.bruggeman@home.nl
+ jfirebaugh@kde.org
+ jeff@caffeinated.me.uk
+****************************************************************************/
+
+/***************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the 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_qsplitter.h" // make sure we get there first
+
+#include <qlayout.h>
+#include <qwidget.h>
+
+#include <kaction.h>
+#include <kapplication.h>
+#include <kdebug.h>
+#include <kfiletreeview.h>
+#include <kfiledialog.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kstdaction.h>
+#include <kinstance.h>
+#include <ktempfile.h>
+#include <kparts/genericfactory.h>
+//#include <ktempdir.h>
+
+#include <kio/netaccess.h>
+
+#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 "kompare_part.h"
+
+typedef KParts::GenericFactory<KomparePart> KomparePartFactory;
+K_EXPORT_COMPONENT_FACTORY( libkomparepart, KomparePartFactory )
+
+ViewSettings* KomparePart::m_viewSettings = 0L;
+DiffSettings* KomparePart::m_diffSettings = 0L;
+
+KomparePart::KomparePart( QWidget *parentWidget, const char *widgetName,
+ QObject *parent, const char *name, const QStringList & /*args*/ ) :
+ KParts::ReadWritePart(parent, name),
+ m_tempDiff( 0 ),
+ m_info()
+{
+ // we need an instance
+ setInstance( KomparePartFactory::instance() );
+
+ if( !m_viewSettings ) {
+ m_viewSettings = new ViewSettings( 0 );
+ }
+ if( !m_diffSettings ) {
+ m_diffSettings = new DiffSettings( 0 );
+ }
+
+ readProperties( kapp->config() );
+
+ // This creates the "Model creator" and connects the signals and slots
+ m_modelList = new Diff2::KompareModelList( m_diffSettings, m_info, this, "komparemodellist" );
+ 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( setModified( bool ) ),
+ this, SLOT( slotSetModified( bool ) ) );
+
+ // 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)) );
+
+ // This creates the splitterwidget and connects the signals and slots
+ m_splitter = new KompareSplitter ( m_viewSettings, parentWidget, widgetName );
+
+ 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()) );
+
+ // notify the part that this is our internal widget
+ setWidget( m_splitter->parentWidget() );
+
+ 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 = new KAction( i18n("Save &All"), "save_all", 0,
+ this, SLOT(saveAll()),
+ actionCollection(), "file_save_all" );
+ m_saveDiff = new KAction( i18n("Save .&diff..."), 0,
+ this, SLOT(saveDiff()),
+ actionCollection(), "file_save_diff" );
+ m_swap = new KAction( i18n( "Swap Source with Destination" ), 0,
+ this, SLOT(slotSwap()),
+ actionCollection(), "file_swap" );
+ m_diffStats = new KAction( i18n( "Show Statistics" ), 0,
+ this, SLOT(slotShowDiffstats()),
+ actionCollection(), "file_diffstats" );
+
+ KStdAction::preferences(this, SLOT(optionsPreferences()), actionCollection());
+}
+
+void KomparePart::updateActions()
+{
+ m_saveAll->setEnabled ( m_modelList->isModified() );
+ 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_diffStats->setEnabled( m_modelList->modelCount() > 0 );
+}
+
+void KomparePart::setEncoding( const QString& encoding )
+{
+ kdDebug() << "Encoding: " << encoding << endl;
+ m_modelList->setEncoding( encoding );
+}
+
+bool KomparePart::openDiff( const KURL& url )
+{
+ kdDebug(8103) << "Url = " << url.url() << endl;
+
+ emit kompareInfo( &m_info );
+
+ m_info.mode = Kompare::ShowingDiff;
+ m_info.source = url;
+ bool result = false;
+ m_info.localSource = fetchURL( url );
+ if ( !m_info.localSource.isEmpty() )
+ {
+ kdDebug(8103) << "Download succeeded " << endl;
+ result = m_modelList->openDiff( m_info.localSource );
+ updateActions();
+ updateCaption();
+ updateStatus();
+ }
+ else
+ {
+ kdDebug(8103) << "Download failed !" << endl;
+ }
+
+ return result;
+}
+
+bool KomparePart::openDiff( const QString& diffOutput )
+{
+ bool value = false;
+
+ emit kompareInfo( &m_info );
+
+ m_info.mode = Kompare::ShowingDiff;
+
+ if ( m_modelList->parseDiffOutput( diffOutput ) == 0 )
+ {
+ value = true;
+ m_modelList->show();
+ updateActions();
+ updateCaption();
+ updateStatus();
+ }
+
+ return value;
+}
+
+bool KomparePart::openDiff3( const KURL& diff3Url )
+{
+ // FIXME: Implement this !!!
+ kdDebug(8103) << "Not implemented yet. Filename is: " << diff3Url.url() << endl;
+ return false;
+}
+
+bool KomparePart::openDiff3( const QString& diff3Output )
+{
+ // FIXME: Implement this !!!
+ kdDebug(8103) << "Not implemented yet. diff3 output is: " << endl;
+ kdDebug(8103) << diff3Output << endl;
+ return false;
+}
+
+bool KomparePart::exists( const QString& url )
+{
+ QFileInfo fi( url );
+ return fi.exists();
+}
+
+const QString KomparePart::fetchURL( const KURL& url )
+{
+ QString tempFileName( "" );
+ if ( !url.isLocalFile() )
+ {
+ if ( ! KIO::NetAccess::download( url, tempFileName, widget() ) )
+ {
+ slotShowError( i18n( "<qt>The URL <b>%1</b> cannot be downloaded.</qt>" ).arg( url.prettyURL() ) );
+ tempFileName = "";
+ }
+ return tempFileName;
+ }
+ else
+ {
+ // is Local already, check if exists
+ if ( exists( url.path() ) )
+ return url.path();
+ else
+ {
+ slotShowError( i18n( "<qt>The URL <b>%1</b> does not exist on your system.</qt>" ).arg( url.prettyURL() ) );
+ return tempFileName;
+ }
+ }
+}
+
+void KomparePart::cleanUpTemporaryFiles()
+{
+ // i hope a local file will not be removed if it was not downloaded...
+ if ( !m_info.localSource.isEmpty() )
+ KIO::NetAccess::removeTempFile( m_info.localSource );
+ if ( !m_info.localDestination.isEmpty() )
+ KIO::NetAccess::removeTempFile( m_info.localDestination );
+}
+
+void KomparePart::compare( const KURL& source, const KURL& destination )
+{
+ m_info.source = source;
+ m_info.destination = destination;
+
+ m_info.localSource = fetchURL( source );
+ m_info.localDestination = fetchURL( destination );
+
+ emit kompareInfo( &m_info );
+
+ if ( !m_info.localSource.isEmpty() && !m_info.localDestination.isEmpty() )
+ {
+ m_modelList->compare( m_info.localSource, m_info.localDestination );
+ updateActions();
+ updateCaption();
+ updateStatus();
+ }
+}
+
+void KomparePart::compareFiles( const KURL& sourceFile, const KURL& destinationFile )
+{
+ emit kompareInfo( &m_info );
+
+ m_info.mode = Kompare::ComparingFiles;
+
+ m_info.source = sourceFile;
+ m_info.destination = destinationFile;
+
+ m_info.localSource = fetchURL( sourceFile );
+ m_info.localDestination = fetchURL( destinationFile );
+
+ if ( !m_info.localSource.isEmpty() && !m_info.localDestination.isEmpty() )
+ {
+ m_modelList->compareFiles( m_info.localSource, m_info.localDestination );
+ updateActions();
+ updateCaption();
+ updateStatus();
+ }
+}
+
+void KomparePart::compareDirs( const KURL& sourceDirectory, const KURL& destinationDirectory )
+{
+ emit kompareInfo( &m_info );
+
+ m_info.mode = Kompare::ComparingDirs;
+
+ m_info.source = sourceDirectory;
+ m_info.destination = destinationDirectory;
+
+ m_info.localSource = fetchURL( sourceDirectory );
+ m_info.localDestination = fetchURL( destinationDirectory );
+
+ if ( !m_info.localSource.isEmpty() && !m_info.localDestination.isEmpty() )
+ {
+ m_modelList->compareDirs( m_info.localSource, m_info.localDestination );
+ updateActions();
+ updateCaption();
+ updateStatus();
+ }
+}
+
+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 )
+{
+ emit kompareInfo( &m_info );
+
+ m_info.source = file;
+ m_info.destination = diffFile;
+
+ m_info.localSource = fetchURL( file );
+ m_info.localDestination = fetchURL( diffFile );
+ m_info.mode = Kompare::BlendingFile;
+
+ if ( !m_info.localSource.isEmpty() && !m_info.localDestination.isEmpty() )
+ {
+ m_modelList->openFileAndDiff( m_info.localSource, m_info.localDestination );
+ updateActions();
+ updateCaption();
+ updateStatus();
+ }
+}
+
+void KomparePart::openDirAndDiff ( const KURL& dir, const KURL& diffFile )
+{
+ emit kompareInfo( &m_info );
+
+ m_info.source = dir;
+ m_info.destination = diffFile;
+
+ m_info.localSource = fetchURL( dir );
+ m_info.localDestination = fetchURL( diffFile );
+ m_info.mode = Kompare::BlendingDir;
+
+ if ( !m_info.localSource.isEmpty() && !m_info.localDestination.isEmpty() )
+ {
+ m_modelList->openDirAndDiff( m_info.localSource, m_info.localDestination );
+ updateActions();
+ updateCaption();
+ updateStatus();
+ }
+}
+
+bool KomparePart::openFile()
+{
+ // This is called from openURL
+ // This is a little inefficient but i will do it anyway
+ openDiff( m_url );
+ return true;
+}
+
+bool KomparePart::saveAll()
+{
+ bool result = m_modelList->saveAll();
+ updateActions();
+ updateCaption();
+ updateStatus();
+ return result;
+}
+
+void KomparePart::saveDiff()
+{
+ KDialogBase* dlg = new KDialogBase( widget(), "save_options",
+ true /* modal */, i18n("Diff Options"),
+ KDialogBase::Ok|KDialogBase::Cancel );
+ KompareSaveOptionsWidget* w = new KompareSaveOptionsWidget(
+ m_info.localSource,
+ m_info.localDestination,
+ m_diffSettings, dlg );
+ dlg->setMainWidget( w );
+ dlg->setButtonOK( KStdGuiItem::save() );
+
+ if( dlg->exec() ) {
+ w->saveOptions();
+ KConfig* config = instance()->config();
+ saveProperties( config );
+ 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, false, widget() ) )
+ {
+ int result = KMessageBox::warningYesNoCancel( widget(), i18n("The file exists or is write-protected; do you want to overwrite it?"), i18n("File Exists"), i18n("Overwrite"), i18n("Do Not Overwrite") );
+ if ( result == KMessageBox::Cancel )
+ {
+ break;
+ }
+ else if ( result == KMessageBox::No )
+ {
+ continue;
+ }
+ else
+ {
+ kdDebug(8103) << "URL = " << url.prettyURL() << endl;
+ kdDebug(8103) << "Directory = " << w->directory() << endl;
+ kdDebug(8103) << "DiffSettings = " << m_diffSettings << endl;
+
+ m_modelList->saveDiff( url.url(), w->directory(), m_diffSettings );
+ break;
+ }
+ }
+ else
+ {
+ kdDebug(8103) << "URL = " << url.prettyURL() << endl;
+ kdDebug(8103) << "Directory = " << w->directory() << endl;
+ kdDebug(8103) << "DiffSettings = " << m_diffSettings << endl;
+
+ m_modelList->saveDiff( url.url(), w->directory(), m_diffSettings );
+ break;
+ }
+ }
+ }
+ delete dlg;
+}
+
+KAboutData *KomparePart::createAboutData()
+{
+ KAboutData *about = new KAboutData("kompare", I18N_NOOP("KomparePart"), "3.2");
+ about->addAuthor("John Firebaugh", "Author", "jfirebaugh@kde.org");
+ about->addAuthor("Otto Bruggeman", "Author", "otto.bruggeman@home.nl" );
+ 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;
+ 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" )
+ .arg( source )
+ .arg( destination );
+ break;
+ case Kompare::ComparingDirs :
+ text = i18n( "Comparing files in %1 with files in %2" )
+ .arg( source )
+ .arg( destination );
+ break;
+ case Kompare::ShowingDiff :
+ text = i18n( "Viewing diff output from %1" ).arg( source );
+ break;
+ case Kompare::BlendingFile :
+ text = i18n( "Blending diff output from %1 into file %2" )
+ .arg( source )
+ .arg( destination );
+ break;
+ case Kompare::BlendingDir :
+ text = i18n( "Blending diff output from %1 into folder %2" )
+ .arg( m_info.source.prettyURL() )
+ .arg( m_info.destination.prettyURL() );
+ break;
+ default:
+ break;
+ }
+
+ emit setStatusBarText( text );
+}
+
+void KomparePart::slotShowError( QString error )
+{
+ KMessageBox::error( widget(), error );
+}
+
+void KomparePart::slotSwap()
+{
+ if ( isModified() )
+ {
+ 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?" ),
+ KStdGuiItem::save(),
+ KStdGuiItem::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
+ KURL url = m_info.source;
+ m_info.source = m_info.destination;
+ m_info.destination = url;
+
+ QString string = m_info.localSource;
+ m_info.localSource = m_info.localDestination;
+ m_info.localDestination = string;
+
+ // Update window caption and statusbar text
+ updateCaption();
+ updateStatus();
+
+ m_modelList->swap();
+}
+
+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::null, false );
+ }
+ 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")
+ .arg(oldFile).arg(newFile).arg(diffFormat)
+ .arg(noOfHunks).arg(noOfDiffs),
+ i18n("Diff Statistics"), QString::null, false );
+ } else { // more than 1 file in diff, or 2 directories compared
+ KMessageBox::information( 0L, i18n(
+ "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")
+ .arg(filesInDiff).arg(diffFormat).arg(oldFile)
+ .arg(newFile).arg(noOfHunks).arg(noOfDiffs),
+ i18n("Diff Statistics"), QString::null, false );
+ }
+}
+
+bool KomparePart::queryClose()
+{
+ if( !isModified() ) 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?" ),
+ KStdGuiItem::save(),
+ KStdGuiItem::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 = new KomparePrefDlg( m_viewSettings, m_diffSettings );
+
+ connect( pref, SIGNAL(applyClicked()), this, SIGNAL(configChanged()) );
+
+ if ( pref->exec() )
+ emit configChanged();
+}
+
+void KomparePart::slotSetModified( bool modified )
+{
+ kdDebug() << "KomparePart::slotSetModified( " << modified << " );" << endl;
+ setModified( modified );
+ updateActions();
+ updateCaption();
+}
+
+#include "kompare_part.moc"
diff --git a/kompare/komparepart/kompare_part.h b/kompare/komparepart/kompare_part.h
new file mode 100644
index 00000000..0a9716c3
--- /dev/null
+++ b/kompare/komparepart/kompare_part.h
@@ -0,0 +1,223 @@
+/***************************************************************************
+ kompare_part.h - description
+ -------------------
+ begin : Sun Mar 4 2001
+ copyright : (C) 2001-2004 Otto Bruggeman
+ (C) 2001-2003 John Firebaugh
+ (C) 2004 Jeff Snyder
+ email : otto.bruggeman@home.nl
+ jfirebaugh@kde.org
+ jeff@caffeinated.me.uk
+****************************************************************************/
+
+/***************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the 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 <kparts/factory.h>
+#include <kparts/part.h>
+
+#include "kompare.h"
+
+#include "kompareinterface.h"
+
+class QWidget;
+
+class KTempFile;
+class KToggleAction;
+class KURL;
+
+namespace Diff2 {
+class Difference;
+class DiffModel;
+class DiffModelList;
+class KompareModelList;
+}
+class DiffSettings;
+class ViewSettings;
+class KFileTreeView;
+class KompareSplitter;
+class KompareProcess;
+
+/**
+ * This is a "Part". It does all the real work in a KPart
+ * application.
+ *
+ * @short Main Part
+ * @author John Firebaugh <jfirebaugh@kde.org>
+ * @author Otto Bruggeman <bruggie@home.nl>
+ * @version 0.3
+ */
+class KomparePart : public KParts::ReadWritePart,
+ public KompareInterface
+{
+ Q_OBJECT
+public:
+ /**
+ * Default constructor
+ */
+ KomparePart( QWidget *parentWidget, const char *widgetName,
+ QObject *parent, const char *name, const QStringList & /*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 dont (can't)
+ // use the readProperties and saveProperties methods
+ virtual bool queryClose();
+
+ // bool isModified() const { return m_modelList->isModified(); }
+ // 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, 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();
+
+ /** This slot is connected to the setModifed( bool ) signal from the KompareModelList */
+ void slotSetModified( bool modified );
+
+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 );
+
+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();
+ 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 optionsPreferences();
+ void updateActions();
+ void updateCaption();
+ void updateStatus();
+
+private:
+ void cleanUpTemporaryFiles();
+ void setupActions();
+ bool exists( const QString& url );
+ bool isDirectory( const KURL& url );
+ const QString fetchURL( const KURL& url );
+
+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;
+
+ KompareSplitter* m_splitter;
+
+ KAction* m_saveAll;
+ KAction* m_saveDiff;
+ KAction* m_swap;
+ KAction* m_diffStats;
+
+ KTempFile* m_tempDiff;
+
+ struct Kompare::Info m_info;
+};
+
+#endif // KOMPAREPART_H
diff --git a/kompare/komparepart/kompare_qsplitter.h b/kompare/komparepart/kompare_qsplitter.h
new file mode 100644
index 00000000..1f1be49d
--- /dev/null
+++ b/kompare/komparepart/kompare_qsplitter.h
@@ -0,0 +1,213 @@
+/****************************************************************************
+**
+**
+** Definition of QSplitter class
+**
+** Created : 980105
+**
+** Copyright (C) 1992-2000 Trolltech AS. All rights reserved.
+**
+** This file is part of the widgets module of the Qt GUI Toolkit.
+**
+** This file may be distributed and/or modified under the terms of the
+** GNU General Public License version 2 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.
+**
+** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
+** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+**
+** See http://www.trolltech.com/gpl/ for GPL licensing information.
+**
+**********************************************************************/
+
+#ifndef QSPLITTER_H
+#define QSPLITTER_H
+
+#ifndef QT_H
+#include "qframe.h"
+#include "qvaluelist.h"
+#endif // QT_H
+
+#ifndef QT_NO_SPLITTER
+
+
+//class QSplitterHandle;
+class QSplitterLayoutStruct;
+class QTextStream;
+
+class QSplitterPrivate
+{
+public:
+ QSplitterPrivate()
+ : opaque( FALSE ), firstShow( TRUE ), childrenCollapsible( TRUE ),
+ handleWidth( 0 ) { }
+
+ QPtrList<QSplitterLayoutStruct> list;
+ bool opaque : 8;
+ bool firstShow : 8;
+ bool childrenCollapsible : 8;
+ int handleWidth;
+};
+
+class Q_EXPORT QSplitter : public QFrame
+{
+ Q_OBJECT
+ Q_PROPERTY( Orientation orientation READ orientation WRITE setOrientation )
+ Q_PROPERTY( bool opaqueResize READ opaqueResize WRITE setOpaqueResize )
+ Q_PROPERTY( int handleWidth READ handleWidth WRITE setHandleWidth )
+ Q_PROPERTY( bool childrenCollapsible READ childrenCollapsible WRITE setChildrenCollapsible )
+
+public:
+ // ### Qt 4.0: remove Auto from public API
+ enum ResizeMode { Stretch, KeepSize, FollowSizeHint, Auto };
+
+ QSplitter( QWidget* parent = 0, const char* name = 0 );
+ QSplitter( Orientation, QWidget* parent = 0, const char* name = 0 );
+ ~QSplitter();
+
+ virtual void setOrientation( Orientation );
+ Orientation orientation() const { return orient; }
+
+ // ### Qt 4.0: make setChildrenCollapsible() and setCollapsible() virtual
+
+ void setChildrenCollapsible( bool );
+ bool childrenCollapsible() const;
+
+ void setCollapsible( QWidget *w, bool );
+ virtual void setResizeMode( QWidget *w, ResizeMode );
+ virtual void setOpaqueResize( bool = TRUE );
+ bool opaqueResize() const;
+
+ void moveToFirst( QWidget * );
+ void moveToLast( QWidget * );
+
+ void refresh() { recalc( TRUE ); }
+ QSize sizeHint() const;
+ QSize minimumSizeHint() const;
+
+ QValueList<int> sizes() const;
+ void setSizes( QValueList<int> );
+
+ int handleWidth() const;
+ void setHandleWidth( int );
+
+protected:
+ void childEvent( QChildEvent * );
+
+ bool event( QEvent * );
+ void resizeEvent( QResizeEvent * );
+
+ int idAfter( QWidget* ) const;
+
+ void moveSplitter( QCOORD pos, int id );
+ virtual void drawSplitter( QPainter*, QCOORD x, QCOORD y,
+ QCOORD w, QCOORD h );
+ void styleChange( QStyle& );
+ int adjustPos( int, int );
+ virtual void setRubberband( int );
+ void getRange( int id, int *, int * );
+
+public: // private (:
+ enum { DefaultResizeMode = 3 };
+
+ void init();
+ void recalc( bool update = FALSE );
+ void doResize();
+ void storeSizes();
+ void getRange( int id, int *, int *, int *, int * );
+ void addContribution( int, int *, int *, bool );
+ int adjustPos( int, int, int *, int *, int *, int * );
+ bool collapsible( QSplitterLayoutStruct * );
+ void processChildEvents();
+ QSplitterLayoutStruct *findWidget( QWidget * );
+ QSplitterLayoutStruct *addWidget( QWidget *, bool prepend = FALSE );
+ void recalcId();
+ void doMove( bool backwards, int pos, int id, int delta, bool upLeft,
+ bool mayCollapse );
+ void setGeo( QWidget *w, int pos, int size, bool splitterMoved );
+ int findWidgetJustBeforeOrJustAfter( int id, int delta, int &collapsibleSize );
+ void updateHandles();
+
+ inline QCOORD pick( const QPoint &p ) const
+ { return orient == Horizontal ? p.x() : p.y(); }
+ inline QCOORD pick( const QSize &s ) const
+ { return orient == Horizontal ? s.width() : s.height(); }
+
+ inline QCOORD trans( const QPoint &p ) const
+ { return orient == Vertical ? p.x() : p.y(); }
+ inline QCOORD trans( const QSize &s ) const
+ { return orient == Vertical ? s.width() : s.height(); }
+
+ QSplitterPrivate *d;
+
+ Orientation orient;
+ friend class QSplitterHandle;
+
+#ifndef QT_NO_TEXTSTREAM
+// moc doesn't like these.
+// friend Q_EXPORT QTextStream& operator<<( QTextStream&, const QSplitter& );
+// friend Q_EXPORT QTextStream& operator>>( QTextStream&, QSplitter& );
+#endif
+
+public:
+#if defined(Q_DISABLE_COPY)
+ QSplitter( const QSplitter & );
+ QSplitter& operator=( const QSplitter & );
+#endif
+};
+
+#ifndef QT_NO_TEXTSTREAM
+Q_EXPORT QTextStream& operator<<( QTextStream&, const QSplitter& );
+Q_EXPORT QTextStream& operator>>( QTextStream&, QSplitter& );
+#endif
+
+class QSplitterHandle : public QWidget
+{
+ Q_OBJECT
+public:
+ QSplitterHandle( Orientation o,
+ QSplitter *parent, const char* name=0 );
+ void setOrientation( Orientation o );
+ Orientation orientation() const { return orient; }
+
+ bool opaque() const { return s->opaqueResize(); }
+
+ QSize sizeHint() const;
+
+ int id() const { return myId; } // d->list.at(id())->wid == this
+ void setId( int i ) { myId = i; }
+
+protected:
+ void paintEvent( QPaintEvent * );
+ void mouseMoveEvent( QMouseEvent * );
+ void mousePressEvent( QMouseEvent * );
+ void mouseReleaseEvent( QMouseEvent * );
+
+public: // private (:
+ Orientation orient;
+ bool opaq;
+ int myId;
+
+ QSplitter *s;
+};
+
+const uint Default = 2;
+
+class QSplitterLayoutStruct : public Qt
+{
+public:
+ QCOORD sizer;
+ uint isHandle : 1;
+ uint collapsible : 2;
+ uint resizeMode : 2;
+ QWidget *wid;
+
+ QSplitterLayoutStruct()
+ : sizer( -1 ), collapsible( Default ) { }
+ QCOORD getSizer( Orientation orient );
+};
+
+#endif // QT_NO_SPLITTER
+
+#endif // QSPLITTER_H
diff --git a/kompare/komparepart/kompareconnectwidget.cpp b/kompare/komparepart/kompareconnectwidget.cpp
new file mode 100644
index 00000000..eed8e99c
--- /dev/null
+++ b/kompare/komparepart/kompareconnectwidget.cpp
@@ -0,0 +1,285 @@
+/***************************************************************************
+ kompareconnectwidget.cpp - description
+ -------------------
+ begin : Tue Jun 26 2001
+ copyright : (C) 2001-2003 John Firebaugh
+ (C) 2001-2004 Otto Bruggeman
+ (C) 2004 Jeff Snyder
+ email : jfirebaugh@kde.org
+ otto.bruggeman@home.nl
+ jeff@caffeinated.me.uk
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the 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 <qapplication.h>
+#include <qpainter.h>
+#include <qpixmap.h>
+#include <qstyle.h>
+#include <qtimer.h>
+
+#include <kdebug.h>
+
+#include "viewsettings.h"
+#include "komparemodellist.h"
+#include "komparelistview.h"
+#include "komparesplitter.h"
+
+#include "kompareconnectwidget.h"
+
+using namespace Diff2;
+
+KompareConnectWidgetFrame::KompareConnectWidgetFrame( KompareListView* left,
+ KompareListView* right,
+ ViewSettings* settings,
+ KompareSplitter* parent,
+ const char* name ) :
+ QSplitterHandle(Horizontal, (QSplitter *)parent, name),
+ m_wid ( left, right, settings, this, name ),
+ m_label ( "", this ),
+ m_layout ( this )
+{
+ 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 ) );
+}
+
+static int kMouseOffset;
+
+void KompareConnectWidgetFrame::mouseMoveEvent( QMouseEvent *e )
+{
+ if ( !(e->state()&LeftButton) )
+ return;
+
+ QCOORD pos = s->pick( parentWidget()->mapFromGlobal(e->globalPos()) )
+ - kMouseOffset;
+
+ ((KompareSplitter*)s)->moveSplitter( pos, id() );
+}
+
+void KompareConnectWidgetFrame::mousePressEvent( QMouseEvent *e )
+{
+ if ( e->button() == LeftButton )
+ kMouseOffset = s->pick( e->pos() );
+ QSplitterHandle::mousePressEvent(e);
+}
+
+void KompareConnectWidgetFrame::mouseReleaseEvent( QMouseEvent *e )
+{
+ if ( !opaque() && e->button() == LeftButton ) {
+ QCOORD pos = s->pick( parentWidget()->mapFromGlobal(e->globalPos()) )
+ - kMouseOffset;
+ ((KompareSplitter*)s)->moveSplitter( pos, id() );
+ }
+}
+
+KompareConnectWidget::KompareConnectWidget( KompareListView* left, KompareListView* right,
+ ViewSettings* settings, QWidget* parent, const char* name )
+ : QWidget(parent, name),
+ m_settings( settings ),
+ m_leftView( left ),
+ m_rightView( right ),
+ m_selectedModel( 0 ),
+ m_selectedDifference( 0 )
+{
+// connect( m_settings, SIGNAL( settingsChanged() ), this, SLOT( slotDelayedRepaint() ) );
+ setBackgroundMode( NoBackground );
+ setSizePolicy( QSizePolicy( QSizePolicy::Fixed, QSizePolicy::Minimum ) );
+ setFocusProxy( parent->parentWidget() );
+}
+
+KompareConnectWidget::~KompareConnectWidget()
+{
+}
+
+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 */ )
+{
+// kdDebug(8106) << "KompareConnectWidget::paintEvent()" << endl;
+
+ QPixmap pixbuf(size());
+ QPainter paint(&pixbuf, this);
+ QPainter* p = &paint;
+
+ p->fillRect( 0, 0, pixbuf.width(), pixbuf.height(), white.dark(110) );
+
+ if ( m_selectedModel )
+ {
+ int firstL = m_leftView->firstVisibleDifference();
+ int firstR = m_rightView->firstVisibleDifference();
+ int lastL = m_leftView->lastVisibleDifference();
+ int lastR = m_rightView->lastVisibleDifference();
+
+ int first = firstL < 0 ? firstR : QMIN( firstL, firstR );
+ int last = lastL < 0 ? lastR : QMAX( lastL, lastR );
+
+// kdDebug(8106) << " left: " << firstL << " - " << lastL << endl;
+// kdDebug(8106) << " right: " << firstR << " - " << lastR << endl;
+// kdDebug(8106) << " drawing: " << first << " - " << last << endl;
+ if ( first >= 0 && last >= 0 && first <= last )
+ {
+ const DifferenceList* differences = const_cast<DiffModel*>(m_selectedModel)->differences();
+ DifferenceListConstIterator diffIt = differences->at( first );
+ DifferenceListConstIterator dEnd = differences->at( last + 1 );
+
+ QRect leftRect, rightRect;
+
+ for ( int i = first; i <= last; ++diffIt, ++i )
+ {
+ Difference* diff = *diffIt;
+ bool selected = ( diff == m_selectedDifference );
+
+ if ( QApplication::reverseLayout() )
+ {
+ leftRect = m_rightView->itemRect( i );
+ rightRect = m_leftView->itemRect( i );
+ }
+ else
+ {
+ leftRect = m_leftView->itemRect( i );
+ rightRect = m_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;
+
+// kdDebug(8106) << "drawing: " << tl << " " << tr << " " << bl << " " << br << endl;
+ QPointArray topBezier = makeTopBezier( tl, tr );
+ QPointArray bottomBezier = makeBottomBezier( bl, br );
+
+ QColor color = m_settings->colorForDifferenceType( diff->type(), selected, diff->applied() ).dark(110);
+ p->setPen( color );
+ p->setBrush( color );
+ p->drawPolygon ( makeConnectPoly( topBezier, bottomBezier ) );
+
+ if ( selected )
+ {
+ p->setPen( color.dark(135) );
+ p->drawPolyline( topBezier );
+ p->drawPolyline( bottomBezier );
+ }
+ }
+ }
+ }
+
+ p->flush();
+ bitBlt(this, 0, 0, &pixbuf);
+}
+
+QPointArray KompareConnectWidget::makeTopBezier( int tl, int tr )
+{
+ int l = 0;
+ int r = width();
+ int o = (int)((double)r*0.4); // 40% of width
+ QPointArray controlPoints;
+
+ if ( tl != tr )
+ {
+ controlPoints.setPoints( 4, l,tl, o,tl, r-o,tr, r,tr );
+ return controlPoints.cubicBezier();
+ }
+ else
+ {
+ controlPoints.setPoints( 2, l,tl, r,tr );
+ return controlPoints;
+ }
+}
+
+QPointArray KompareConnectWidget::makeBottomBezier( int bl, int br )
+{
+ int l = 0;
+ int r = width();
+ int o = (int)((double)r*0.4); // 40% of width
+ QPointArray controlPoints;
+
+ if ( bl != br )
+ {
+ controlPoints.setPoints( 4, r,br, r-o,br, o,bl, l,bl );
+ return controlPoints.cubicBezier();
+ }
+ else
+ {
+ controlPoints.setPoints( 2, r,br, l,bl );
+ return controlPoints;
+ }
+}
+
+QPointArray KompareConnectWidget::makeConnectPoly( const QPointArray& topBezier, const QPointArray& bottomBezier )
+{
+ QPointArray poly( topBezier.size() + bottomBezier.size() );
+ for( uint i = 0; i < topBezier.size(); i++ )
+ poly.setPoint( i, topBezier.point( i ) );
+ for( uint i = 0; i < bottomBezier.size(); i++ )
+ poly.setPoint( i + topBezier.size(), bottomBezier.point( i ) );
+
+ return poly;
+}
+
+#include "kompareconnectwidget.moc"
diff --git a/kompare/komparepart/kompareconnectwidget.h b/kompare/komparepart/kompareconnectwidget.h
new file mode 100644
index 00000000..96160c97
--- /dev/null
+++ b/kompare/komparepart/kompareconnectwidget.h
@@ -0,0 +1,95 @@
+/***************************************************************************
+ kompareconnectwidget.h - description
+ -------------------
+ begin : Tue Jun 26 2001
+ copyright : (C) 2001-2003 John Firebaugh
+ (C) 2001-2004 Otto Bruggeman
+ (C) 2004 Jeff Snyder
+ email : jfirebaugh@kde.org
+ otto.bruggeman@home.nl
+ jeff@caffeinated.me.uk
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the 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 "kompare_qsplitter.h"
+#include <qwidget.h>
+
+namespace Diff2 {
+class DiffModel;
+}
+class ViewSettings;
+class KompareListView;
+class KompareSplitter;
+
+class KompareConnectWidget : public QWidget
+{
+ Q_OBJECT
+
+public:
+ KompareConnectWidget( KompareListView* left, KompareListView* right,
+ 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 );
+ QPointArray makeTopBezier( int tl, int tr );
+ QPointArray makeBottomBezier( int bl, int br );
+ QPointArray makeConnectPoly( const QPointArray& topBezier, const QPointArray& bottomBezier );
+
+private:
+ ViewSettings* m_settings;
+
+ KompareListView* m_leftView;
+ KompareListView* m_rightView;
+
+ const Diff2::DiffModel* m_selectedModel;
+ const Diff2::Difference* m_selectedDifference;
+};
+
+class KompareConnectWidgetFrame : public QSplitterHandle
+{
+ Q_OBJECT
+public:
+ KompareConnectWidgetFrame( KompareListView* left, KompareListView* right,
+ 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 */ ) { }
+
+ void mouseMoveEvent( QMouseEvent * );
+ void mousePressEvent( QMouseEvent * );
+ void mouseReleaseEvent( QMouseEvent * );
+
+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..b86bdef9
--- /dev/null
+++ b/kompare/komparepart/komparelistview.cpp
@@ -0,0 +1,783 @@
+/***************************************************************************
+ komparelistview.h - description
+ -------------------
+ begin : Sun Mar 4 2001
+ copyright : (C) 2001-2004 Otto Bruggeman
+ (C) 2001-2003 John Firebaugh
+ (C) 2004 Jeff Snyder
+ email : otto.bruggeman@home.nl
+ jfirebaugh@kde.org
+ jeff@caffeinated.me.uk
+****************************************************************************/
+
+/***************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the 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 <qheader.h>
+#include <qpainter.h>
+#include <qregexp.h>
+#include <qtimer.h>
+
+#include <kdebug.h>
+#include <kglobal.h>
+#include <kglobalsettings.h>
+
+#include "diffmodel.h"
+#include "diffhunk.h"
+#include "difference.h"
+#include "viewsettings.h"
+#include "komparemodellist.h"
+#include "komparesplitter.h"
+
+#include "komparelistview.h"
+
+#define COL_LINE_NO 0
+#define COL_MAIN 1
+
+#define BLANK_LINE_HEIGHT 3
+#define HUNK_LINE_HEIGHT 5
+
+using namespace Diff2;
+
+KompareListViewFrame::KompareListViewFrame( bool isSource,
+ ViewSettings* settings,
+ KompareSplitter* parent,
+ const char* name ):
+ QFrame ( parent, name ),
+ 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 );
+ }
+}
+
+KompareListView::KompareListView( bool isSource,
+ ViewSettings* settings,
+ QWidget* parent, const char* name ) :
+ KListView( parent, name ),
+ m_isSource( isSource ),
+ m_settings( settings ),
+ m_scrollId( -1 ),
+ m_selectedModel( 0 ),
+ m_selectedDifference( 0 )
+{
+ header()->hide();
+ addColumn( "Line Number", 0 );
+ addColumn( "Main" );
+ addColumn( "Blank" );
+ setColumnAlignment( COL_LINE_NO, AlignRight );
+ setAllColumnsShowFocus( true );
+ setRootIsDecorated( false );
+ setSorting( -1 );
+ setItemMargin( 3 );
+ setTreeStepSize( 0 );
+ setColumnWidthMode( COL_LINE_NO, Maximum );
+ setColumnWidthMode( COL_MAIN, Maximum );
+ setResizeMode( LastColumn );
+ setFrameStyle( QFrame::NoFrame );
+ setVScrollBarMode( QScrollView::AlwaysOff );
+ setHScrollBarMode( QScrollView::AlwaysOff );
+ setFocusPolicy( QWidget::NoFocus );
+ setFont( m_settings->m_font );
+ setSpaces( m_settings->m_tabToNumberOfSpaces );
+ setFocusProxy( parent->parentWidget() );
+}
+
+KompareListView::~KompareListView()
+{
+}
+
+KompareListViewItem* KompareListView::itemAtIndex( int i )
+{
+ return m_items[ i ];
+}
+
+int KompareListView::firstVisibleDifference()
+{
+ QListViewItem* item = itemAt( QPoint( 0, 0 ) );
+
+ if( item == 0 )
+ {
+ kdDebug(8104) << "no item at viewport coordinates (0,0)" << endl;
+ }
+
+ while( item ) {
+ KompareListViewLineItem* lineItem = dynamic_cast<KompareListViewLineItem*>(item);
+ if( lineItem && lineItem->diffItemParent()->difference()->type() != Difference::Unchanged )
+ break;
+ item = item->itemBelow();
+ }
+
+ if( item )
+ return m_items.findIndex( ((KompareListViewLineItem*)item)->diffItemParent() );
+
+ return -1;
+}
+
+int KompareListView::lastVisibleDifference()
+{
+ QListViewItem* item = itemAt( QPoint( 0, visibleHeight() - 1 ) );
+
+ if( item == 0 )
+ {
+ kdDebug(8104) << "no item at viewport coordinates (0," << visibleHeight() - 1 << ")" << endl;
+ item = lastItem();
+ }
+
+ while( item ) {
+ KompareListViewLineItem* lineItem = dynamic_cast<KompareListViewLineItem*>(item);
+ if( lineItem && lineItem->diffItemParent()->difference()->type() != Difference::Unchanged )
+ break;
+ item = item->itemAbove();
+ }
+
+ if( item )
+ return m_items.findIndex( ((KompareListViewLineItem*)item)->diffItemParent() );
+
+ return -1;
+}
+
+QRect KompareListView::itemRect( int i )
+{
+ QListViewItem* item = itemAtIndex( i );
+ int x = 0;
+ int y = itemPos( item );
+ int vx, vy;
+ contentsToViewport( x, y, vx, vy );
+ return QRect( vx, vy, 0, item->totalHeight() );
+}
+
+int KompareListView::minScrollId()
+{
+ return visibleHeight() / 2;
+}
+
+int KompareListView::maxScrollId()
+{
+ KompareListViewItem* item = (KompareListViewItem*)firstChild();
+ if(!item) return 0;
+ while( item && item->nextSibling() ) {
+ item = (KompareListViewItem*)item->nextSibling();
+ }
+ int maxId = item->scrollId() + item->maxHeight() - minScrollId();
+ kdDebug(8104) << "Max ID = " << maxId << endl;
+ return maxId;
+}
+
+int KompareListView::contentsWidth()
+{
+ return ( columnWidth(COL_LINE_NO) + columnWidth(COL_MAIN) );
+}
+
+void KompareListView::setXOffset( int x )
+{
+ kdDebug(8104) << "SetXOffset : Scroll to x position: " << x << endl;
+ setContentsPos( x, contentsY() );
+}
+
+void KompareListView::scrollToId( int id )
+{
+// kdDebug(8104) << "ScrollToID : Scroll to id : " << id << endl;
+ KompareListViewItem* item = (KompareListViewItem*)firstChild();
+ while( item && item->nextSibling() ) {
+ if( ((KompareListViewItem*)item->nextSibling())->scrollId() > id )
+ break;
+ item = (KompareListViewItem*)item->nextSibling();
+ }
+
+ if( item ) {
+ int pos = item->itemPos();
+ int itemId = item->scrollId();
+ int height = item->totalHeight();
+ double r = (double)( id - itemId ) / (double)item->maxHeight();
+ int y = pos + (int)( r * (double)height ) - minScrollId();
+// kdDebug(8104) << "scrollToID: " << endl;
+// kdDebug(8104) << " id = " << id << endl;
+// kdDebug(8104) << " id after = " << ( item->nextSibling() ? QString::number( ((KompareListViewItem*)item->nextSibling())->scrollId() ) : "no such sibling..." ) << endl;
+// kdDebug(8104) << " pos = " << pos << endl;
+// kdDebug(8104) << " itemId = " << itemId << endl;
+// kdDebug(8104) << " r = " << r << endl;
+// kdDebug(8104) << " height = " << height << endl;
+// kdDebug(8104) << " minID = " << minScrollId() << endl;
+// kdDebug(8104) << " y = " << y << endl;
+// kdDebug(8104) << "contentsHeight = " << contentsHeight() << endl;
+// kdDebug(8104) << " c - y = " << contentsHeight() - y << endl;
+ setContentsPos( contentsX(), 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 )
+{
+ kdDebug(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 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[ (void*)diff ];
+ if( !item ) {
+ kdDebug(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());
+ setSelected( item, true );
+}
+
+void KompareListView::slotSetSelection( const Difference* diff )
+{
+ kdDebug(8104) << "KompareListView::slotSetSelection( const Difference* diff )" << endl;
+
+ setSelectedDifference( diff, true );
+}
+
+void KompareListView::slotSetSelection( const DiffModel* model, const Difference* diff )
+{
+ kdDebug(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;
+
+ m_itemDict.resize(model->differenceCount());
+
+ DiffHunkListConstIterator hunkIt = model->hunks()->begin();
+ DiffHunkListConstIterator hEnd = model->hunks()->end();
+
+ KompareListViewItem* item = 0;
+ Difference* tmpdiff = 0;
+ DiffHunk* hunk = 0;
+
+
+ for ( ; hunkIt != hEnd; ++hunkIt )
+ {
+ hunk = *hunkIt;
+
+ if( item )
+ item = new KompareListViewHunkItem( this, item, hunk, model->isBlended() );
+ else
+ item = new KompareListViewHunkItem( this, hunk, model->isBlended() );
+
+ DifferenceListConstIterator diffIt = hunk->differences().begin();
+ DifferenceListConstIterator dEnd = hunk->differences().end();
+
+ for ( ; diffIt != dEnd; ++diffIt )
+ {
+ tmpdiff = *diffIt;
+
+ item = new KompareListViewDiffItem( this, item, tmpdiff );
+
+ int type = tmpdiff->type();
+
+ if ( type != Difference::Unchanged )
+ {
+ m_items.append( (KompareListViewDiffItem*)item );
+ m_itemDict.insert( tmpdiff, (KompareListViewDiffItem*)item );
+ }
+ }
+ }
+
+ slotSetSelection( diff );
+}
+
+void KompareListView::contentsMousePressEvent( QMouseEvent* e )
+{
+ QPoint vp = contentsToViewport( e->pos() );
+ KompareListViewLineItem* lineItem = dynamic_cast<KompareListViewLineItem*>( itemAt( vp ) );
+ if( !lineItem )
+ return;
+ KompareListViewDiffItem* diffItem = lineItem->diffItemParent();
+ if( diffItem->difference()->type() != Difference::Unchanged ) {
+ emit differenceClicked( diffItem->difference() );
+ }
+}
+
+void KompareListView::contentsMouseDoubleClickEvent( QMouseEvent* e )
+{
+ QPoint vp = contentsToViewport( e->pos() );
+ KompareListViewLineItem* lineItem = dynamic_cast<KompareListViewLineItem*>( itemAt( vp ) );
+ if ( !lineItem )
+ return;
+ KompareListViewDiffItem* diffItem = lineItem->diffItemParent();
+ if ( diffItem->difference()->type() != Difference::Unchanged ) {
+ // FIXME: make a new signal that does both
+ emit differenceClicked( diffItem->difference() );
+ emit applyDifference( !diffItem->difference()->applied() );
+ }
+}
+
+void KompareListView::slotApplyDifference( bool apply )
+{
+ m_itemDict[ (void*)m_selectedDifference ]->applyDifference( apply );
+}
+
+void KompareListView::slotApplyAllDifferences( bool apply )
+{
+ QPtrDictIterator<KompareListViewDiffItem> it ( m_itemDict );
+ for( ; it.current(); ++it )
+ it.current()->applyDifference( apply );
+ repaint();
+}
+
+void KompareListView::slotApplyDifference( const Difference* diff, bool apply )
+{
+ m_itemDict[ (void*)diff ]->applyDifference( apply );
+}
+
+void KompareListView::setSpaces( int spaces )
+{
+ m_spaces.truncate( 0 );
+ kdDebug( 8104 ) << "tabToNumberOfSpaces: " << spaces << endl;
+ for ( int i = 0; i < spaces; i++ )
+ m_spaces += " ";
+
+ triggerUpdate();
+}
+
+void KompareListView::wheelEvent( QWheelEvent* e )
+{
+ e->ignore(); // we want the parent to catch wheel events
+}
+
+void KompareListView::resizeEvent( QResizeEvent* e )
+{
+ KListView::resizeEvent(e);
+ emit resized();
+ kdDebug() << "resizeEvent " << endl;
+}
+
+KompareListViewItem::KompareListViewItem( KompareListView* parent )
+ : QListViewItem( parent ),
+ m_scrollId( 0 )
+{
+// kdDebug(8104) << "Created KompareListViewItem with scroll id " << m_scrollId << endl;
+}
+
+KompareListViewItem::KompareListViewItem( KompareListView* parent, KompareListViewItem* after )
+ : QListViewItem( parent, after ),
+ m_scrollId( after->scrollId() + after->maxHeight() )
+{
+// kdDebug(8104) << "Created KompareListViewItem with scroll id " << m_scrollId << endl;
+}
+
+KompareListViewItem::KompareListViewItem( KompareListViewItem* parent )
+ : QListViewItem( parent ),
+ m_scrollId( 0 )
+{
+}
+
+KompareListViewItem::KompareListViewItem( KompareListViewItem* parent, KompareListViewItem* /*after*/ )
+ : QListViewItem( parent ),
+ m_scrollId( 0 )
+{
+}
+
+KompareListView* KompareListViewItem::kompareListView() const
+{
+ return (KompareListView*)listView();
+}
+
+void KompareListViewItem::paintFocus( QPainter* /* p */, const QColorGroup& /* cg */, const QRect& /* r */ )
+{
+ // Don't paint anything
+}
+
+KompareListViewDiffItem::KompareListViewDiffItem( KompareListView* parent, Difference* difference )
+ : KompareListViewItem( parent ),
+ m_difference( difference ),
+ m_sourceItem( 0L ),
+ m_destItem( 0L )
+{
+ init();
+}
+
+KompareListViewDiffItem::KompareListViewDiffItem( KompareListView* parent, KompareListViewItem* after, Difference* difference )
+ : KompareListViewItem( parent, after ),
+ m_difference( difference ),
+ m_sourceItem( 0L ),
+ m_destItem( 0L )
+{
+ init();
+}
+
+void KompareListViewDiffItem::init()
+{
+ setExpandable( true );
+ setOpen( true );
+ m_destItem = new KompareListViewLineContainerItem( this, false );
+ m_sourceItem = new KompareListViewLineContainerItem( this, true );
+ setVisibility();
+}
+
+void KompareListViewDiffItem::setup()
+{
+ KompareListViewItem::setup();
+ setHeight( 0 );
+}
+
+void KompareListViewDiffItem::setVisibility()
+{
+ m_sourceItem->setVisible( kompareListView()->isSource() || m_difference->applied() );
+ m_destItem->setVisible( !m_sourceItem->isVisible() );
+}
+
+void KompareListViewDiffItem::applyDifference( bool apply )
+{
+ kdDebug(8104) << "KompareListViewDiffItem::applyDifference( " << apply << " )" << endl;
+ setVisibility();
+ setup();
+ repaint();
+}
+
+int KompareListViewDiffItem::maxHeight()
+{
+ int lines = QMAX( m_difference->sourceLineCount(), m_difference->destinationLineCount() );
+ if( lines == 0 )
+ return BLANK_LINE_HEIGHT;
+ else
+ return lines * listView()->fontMetrics().lineSpacing();
+}
+
+void KompareListViewDiffItem::setSelected( bool b )
+{
+ kdDebug(8104) << "KompareListViewDiffItem::setSelected( " << b << " )" << endl;
+ KompareListViewItem::setSelected( b );
+ QListViewItem* item = m_sourceItem->isVisible() ?
+ m_sourceItem->firstChild() :
+ m_destItem->firstChild();
+ while( item && item->isVisible() ) {
+ item->repaint();
+ item = item->nextSibling();
+ }
+}
+
+KompareListViewLineContainerItem::KompareListViewLineContainerItem( KompareListViewDiffItem* parent, bool isSource )
+ : KompareListViewItem( parent ),
+ m_isSource( isSource )
+{
+// kdDebug(8104) << "isSource ? " << (isSource ? " Yes!" : " No!") << endl;
+ setExpandable( true );
+ setOpen( true );
+
+ int lines = lineCount();
+ int line = lineNumber() + lines - 1;
+// kdDebug(8104) << "LineNumber : " << lineNumber() << endl;
+ if( lines == 0 ) {
+ new KompareListViewBlankLineItem( this );
+ return;
+ }
+
+ for( int i = lines - 1; i >= 0; i--, line-- ) {
+ new KompareListViewLineItem( this, line, lineAt( i ) );
+ }
+}
+
+void KompareListViewLineContainerItem::setup()
+{
+ KompareListViewItem::setup();
+ setHeight( 0 );
+}
+
+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 )
+{
+ setText( COL_LINE_NO, QString::number( line ) );
+ setText( COL_MAIN, text->string() );
+ m_text = text;
+}
+
+void KompareListViewLineItem::setup()
+{
+ KompareListViewItem::setup();
+ setHeight( listView()->fontMetrics().lineSpacing() );
+}
+
+void KompareListViewLineItem::paintCell( QPainter * p, const QColorGroup & cg, int column, int width, int align )
+{
+ QColor bg = cg.base();
+ p->fillRect( 0, 0, width, height(), bg );
+ if ( diffItemParent()->difference()->type() == Difference::Unchanged )
+ {
+ if ( column == COL_LINE_NO )
+ {
+ bg = cg.background();
+ p->fillRect( 0, 0, width, height(), bg );
+ }
+ }
+ else
+ {
+ bg = kompareListView()->settings()->colorForDifferenceType(
+ diffItemParent()->difference()->type(),
+ diffItemParent()->isSelected(),
+ diffItemParent()->difference()->applied() );
+ if ( column != COL_MAIN )
+ p->fillRect( 0, 0, width, height(), bg );
+ }
+
+ p->setPen( cg.foreground() );
+
+ paintText( p, bg, column, width, align );
+
+ if ( diffItemParent()->isSelected() )
+ {
+ p->setPen( bg.dark(135) );
+ if ( this == parent()->firstChild() )
+ p->drawLine( 0, 0, width, 0 );
+ if ( nextSibling() == 0 )
+ p->drawLine( 0, height() - 1, width, height() - 1 );
+ }
+}
+
+void KompareListViewLineItem::paintText( QPainter* p, const QColor& bg, int column, int width, int align )
+{
+ if ( column == COL_MAIN )
+ {
+ QString textChunk;
+ int offset = listView()->itemMargin();
+ unsigned int prevValue = 0;
+ int chunkWidth;
+ QBrush changeBrush( bg, Dense3Pattern );
+ QBrush normalBrush( bg, SolidPattern );
+ QBrush brush;
+
+ if ( m_text->string().isEmpty() )
+ {
+ p->fillRect( 0, 0, width, height(), normalBrush );
+ return;
+ }
+
+ p->fillRect( 0, 0, offset, height(), 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 );
+// kdDebug(8104) << "TextChunk = \"" << textChunk << "\"" << endl;
+// kdDebug(8104) << "c->offset() = " << c->offset() << endl;
+// kdDebug(8104) << "prevValue = " << prevValue << endl;
+ textChunk.replace( QChar('\t'), kompareListView()->spaces() );
+ 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, height(), brush );
+ p->drawText( offset, 0,
+ chunkWidth, height(),
+ align, textChunk );
+ offset += chunkWidth;
+ }
+ }
+ if ( prevValue < m_text->string().length() )
+ {
+ // Still have to draw some string without changes
+ textChunk = m_text->string().mid( prevValue, kMax( ( unsigned int )1, m_text->string().length() - prevValue ) );
+ textChunk.replace( QChar('\t'), kompareListView()->spaces() );
+// kdDebug(8104) << "TextChunk = \"" << textChunk << "\"" << endl;
+ QFont font( p->font() );
+ font.setBold( false );
+ p->setFont( font );
+ chunkWidth = p->fontMetrics().width( textChunk );
+ p->fillRect( offset, 0, chunkWidth, height(), normalBrush );
+ p->drawText( offset, 0,
+ chunkWidth, height(),
+ align, textChunk );
+ offset += chunkWidth;
+ }
+ p->fillRect( offset, 0, width - offset, height(), normalBrush );
+ }
+ else
+ {
+ p->fillRect( 0, 0, width, height(), bg );
+ p->drawText( listView()->itemMargin(), 0,
+ width - listView()->itemMargin(), height(),
+ align, text( column ) );
+ }
+}
+
+KompareListViewDiffItem* KompareListViewLineItem::diffItemParent() const
+{
+ KompareListViewLineContainerItem* p = (KompareListViewLineContainerItem*)parent();
+ return p->diffItemParent();
+}
+
+KompareListViewBlankLineItem::KompareListViewBlankLineItem( KompareListViewLineContainerItem* parent )
+ : KompareListViewLineItem( parent, 0, new DifferenceString() )
+{
+}
+
+void KompareListViewBlankLineItem::setup()
+{
+ KompareListViewLineItem::setup();
+ setHeight( BLANK_LINE_HEIGHT );
+}
+
+void KompareListViewBlankLineItem::paintText( QPainter* p, const QColor& bg, int column, int width, int )
+{
+ if ( column == COL_MAIN )
+ {
+ QBrush normalBrush( bg, SolidPattern );
+ p->fillRect( 0, 0, width, height(), normalBrush );
+ }
+}
+
+KompareListViewHunkItem::KompareListViewHunkItem( KompareListView* parent, DiffHunk* hunk, bool zeroHeight )
+ : KompareListViewItem( parent ),
+ m_zeroHeight( zeroHeight ),
+ m_hunk( hunk )
+{
+ setSelectable( false );
+}
+
+KompareListViewHunkItem::KompareListViewHunkItem( KompareListView* parent, KompareListViewItem* after, DiffHunk* hunk, bool zeroHeight )
+ : KompareListViewItem( parent, after ),
+ m_zeroHeight( zeroHeight ),
+ m_hunk( hunk )
+{
+ setSelectable( false );
+}
+
+int KompareListViewHunkItem::maxHeight()
+{
+ if( m_zeroHeight ) {
+ return 0;
+ } else if( m_hunk->function().isEmpty() ) {
+ return HUNK_LINE_HEIGHT;
+ } else {
+ return listView()->fontMetrics().lineSpacing();
+ }
+}
+
+void KompareListViewHunkItem::setup()
+{
+ KompareListViewItem::setup();
+
+ setHeight( maxHeight() );
+}
+
+void KompareListViewHunkItem::paintCell( QPainter * p, const QColorGroup & cg, int column, int width, int align )
+{
+ p->fillRect( 0, 0, width, height(), cg.mid() );
+ if( column == COL_MAIN ) {
+ p->drawText( listView()->itemMargin(), 0, width - listView()->itemMargin(), height(),
+ 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..26a6dacb
--- /dev/null
+++ b/kompare/komparepart/komparelistview.h
@@ -0,0 +1,225 @@
+/***************************************************************************
+ komparelistview.h - description
+ -------------------
+ begin : Sun Mar 4 2001
+ copyright : (C) 2001-2004 Otto Bruggeman
+ (C) 2001-2003 John Firebaugh
+ (C) 2004 Jeff Snyder
+ email : otto.bruggeman@home.nl
+ jfirebaugh@kde.org
+ jeff@caffeinated.me.uk
+****************************************************************************/
+
+/***************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the 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 <qptrlist.h>
+#include <qptrdict.h>
+#include <qlabel.h>
+#include <qlayout.h>
+
+#include <klistview.h>
+
+namespace Diff2 {
+class DiffModel;
+class DiffHunk;
+class Difference;
+class DifferenceString;
+class KompareModelList;
+}
+class ViewSettings;
+class KompareSplitter;
+class KompareListView;
+class KompareListViewItem;
+class KompareListViewDiffItem;
+class KompareListViewLineContainerItem;
+
+class KompareListView : public KListView
+{
+ Q_OBJECT
+
+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 contentsWidth();
+
+ bool isSource() const { return m_isSource; };
+ ViewSettings* settings() const { return m_settings; };
+
+ void setSelectedDifference( const Diff2::Difference* diff, bool scroll );
+
+ const QString& spaces() const { return m_spaces; };
+ void setSpaces( int spaces );
+
+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:
+ void wheelEvent( QWheelEvent* e );
+ void resizeEvent( QResizeEvent* e );
+ void contentsMousePressEvent ( QMouseEvent * e );
+ void contentsMouseDoubleClickEvent ( QMouseEvent* );
+ void contentsMouseReleaseEvent ( QMouseEvent * ) {};
+ void contentsMouseMoveEvent ( QMouseEvent * ) {};
+
+private:
+ QValueList<KompareListViewDiffItem*> m_items;
+ QPtrDict<KompareListViewDiffItem> m_itemDict;
+ bool m_isSource;
+ ViewSettings* m_settings;
+ int m_scrollId;
+ int m_maxMainWidth;
+ const Diff2::DiffModel* m_selectedModel;
+ const Diff2::Difference* m_selectedDifference;
+ QString m_spaces;
+};
+
+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 KompareListViewItem : public QListViewItem
+{
+public:
+ KompareListViewItem( KompareListView* parent );
+ KompareListViewItem( KompareListView* parent, KompareListViewItem* after );
+ KompareListViewItem( KompareListViewItem* parent );
+ KompareListViewItem( KompareListViewItem* parent, KompareListViewItem* after );
+
+ void paintFocus( QPainter* p, const QColorGroup& cg, const QRect& r );
+ int scrollId() { return m_scrollId; };
+
+ virtual int maxHeight() = 0;
+
+ KompareListView* kompareListView() const;
+
+private:
+ int m_scrollId;
+};
+
+class KompareListViewDiffItem : public KompareListViewItem
+{
+public:
+ KompareListViewDiffItem( KompareListView* parent, Diff2::Difference* difference );
+ KompareListViewDiffItem( KompareListView* parent, KompareListViewItem* after, Diff2::Difference* difference );
+
+ void setup();
+ void setSelected( bool b );
+ void applyDifference( bool apply );
+
+ Diff2::Difference* difference() { return m_difference; };
+
+ int maxHeight();
+
+private:
+ void init();
+ void setVisibility();
+
+ Diff2::Difference* m_difference;
+ KompareListViewLineContainerItem* m_sourceItem;
+ KompareListViewLineContainerItem* m_destItem;
+};
+
+class KompareListViewLineContainerItem : public KompareListViewItem
+{
+public:
+ KompareListViewLineContainerItem( KompareListViewDiffItem* parent, bool isSource );
+
+ void setup();
+ int maxHeight() { return 0; }
+ KompareListViewDiffItem* diffItemParent() const;
+
+private:
+ int lineCount() const;
+ int lineNumber() const;
+ Diff2::DifferenceString* lineAt( int i ) const;
+
+ bool m_isSource;
+};
+
+class KompareListViewLineItem : public KompareListViewItem
+{
+public:
+ KompareListViewLineItem( KompareListViewLineContainerItem* parent, int line, Diff2::DifferenceString* text );
+
+ virtual void setup();
+ int maxHeight() { return 0; }
+
+ virtual void paintCell( QPainter* p, const QColorGroup& cg, int column, int width, int align );
+ virtual void paintText( QPainter* p, const QColor& bg, int column, int width, int align );
+
+ KompareListViewDiffItem* diffItemParent() const;
+
+private:
+ Diff2::DifferenceString* m_text;
+};
+
+class KompareListViewBlankLineItem : public KompareListViewLineItem
+{
+public:
+ KompareListViewBlankLineItem( KompareListViewLineContainerItem* parent );
+
+ void setup();
+
+ 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 );
+
+ void setup();
+ void paintCell( QPainter* p, const QColorGroup& cg, int column, int width, int align );
+
+ 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..3d075d62
--- /dev/null
+++ b/kompare/komparepart/komparepart.desktop
@@ -0,0 +1,24 @@
+[Desktop Entry]
+GenericComment=A part to graphically show the difference between 2 files
+Name=KomparePart
+Name[af]=K-vergelyk-deel
+Name[de]=Einbettungsfähige Komponente von Kompare
+Name[eo]=Komparo-parto
+Name[he]=רכיב Kompare
+Name[hi]=काम्पेयर-पार्ट
+Name[lv]=SalīdzinātDaļu
+Name[pl]=Moduł Kompare
+Name[pt_BR]=Parte do Kompare
+Name[ro]=Componentă Kompare
+Name[ru]=Компонент утилиты сравнения файлов
+Name[sv]=Kompare-del
+Name[ta]=கோம்பெர் உறுப்பு
+Name[tg]=Қисмати утилитҳои баробаркунии файлҳо
+Name[th]=ส่วนเปรียบเทียบ
+Name[zu]=Ingxenye yeKompare
+MimeType=text/x-diff
+ServiceTypes=Kompare/ViewPart,KParts/ReadWritePart,KParts/ReadOnlyPart
+X-KDE-Library=libkomparepart
+Type=Service
+Icon=kompare
+InitialPreference=10
diff --git a/kompare/komparepart/komparepartui.rc b/kompare/komparepart/komparepartui.rc
new file mode 100644
index 00000000..c7140325
--- /dev/null
+++ b/kompare/komparepart/komparepartui.rc
@@ -0,0 +1,44 @@
+<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
+<kpartgui name="kompare_part" version="5">
+<MenuBar>
+ <Menu name="file"><text>&amp;File</text>
+ <Action name="file_save"/>
+ <Action name="file_save_all"/>
+ <Action name="file_save_as"/>
+ <Action name="file_save_diff"/>
+ <Separator/>
+ <Action name="file_swap"/>
+ <Action name="file_diffstats"/>
+ </Menu>
+ <Menu name="difference"><text>&amp;Difference</text>
+ <Action name="difference_unapplyall"/>
+ <Action name="difference_unapply"/>
+ <Action name="difference_apply"/>
+ <Action name="difference_applyall"/>
+ <Separator/>
+ <Action name="difference_previousfile"/>
+ <Action name="difference_nextfile"/>
+ <Separator/>
+ <Action name="difference_previous"/>
+ <Action name="difference_next"/>
+ </Menu>
+ <Menu name="settings"><text>&amp;Settings</text>
+ <Action name="options_configure"/>
+ </Menu>
+</MenuBar>
+<ToolBar name="mainToolBar">
+ <Action name="file_save"/>
+ <Action name="file_save_all"/>
+ <Separator/>
+ <Action name="difference_previousfile"/>
+ <Action name="difference_nextfile"/>
+ <Separator/>
+ <Action name="difference_previous"/>
+ <Action name="difference_next"/>
+ <Separator/>
+ <Action name="difference_unapplyall"/>
+ <Action name="difference_unapply"/>
+ <Action name="difference_apply"/>
+ <Action name="difference_applyall"/>
+</ToolBar>
+</kpartgui>
diff --git a/kompare/komparepart/kompareprefdlg.cpp b/kompare/komparepart/kompareprefdlg.cpp
new file mode 100644
index 00000000..2d731af3
--- /dev/null
+++ b/kompare/komparepart/kompareprefdlg.cpp
@@ -0,0 +1,106 @@
+/***************************************************************************
+ kompareprefdlg.cpp - description
+ -------------------
+ begin : Sun Mar 4 2001
+ copyright : (C) 2001-2003 by Otto Bruggeman
+ and John Firebaugh
+ email : otto.bruggeman@home.nl
+ jfirebaugh@kde.org
+****************************************************************************/
+
+/***************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+***************************************************************************/
+
+#include <qvbox.h>
+
+#include <kdebug.h>
+#include <kiconloader.h>
+#include <klocale.h>
+
+#include "diffpage.h"
+#include "viewpage.h"
+
+#include "kompareprefdlg.h"
+
+// implementation
+
+KomparePrefDlg::KomparePrefDlg( ViewSettings* viewSets, DiffSettings* diffSets ) : KDialogBase( IconList, i18n( "Preferences" ), Help|Default|Ok|Apply|Cancel, Ok, 0, 0, true, true )
+{
+ // ok i need some stuff in that pref dlg...
+ setIconListAllVisible(true);
+
+ QVBox* frame;
+ frame = addVBoxPage( i18n( "View" ), i18n( "View Settings" ), UserIcon( "diff_view" ) );
+ m_viewPage = new ViewPage( frame );
+ m_viewPage->setSettings( viewSets );
+
+ frame = addVBoxPage( i18n( "Diff" ), i18n( "Diff Settings" ), UserIcon( "diff_specific" ) );
+ m_diffPage = new DiffPage( frame );
+ m_diffPage->setSettings( diffSets );
+
+// frame = addVBoxPage( i18n( "" ), i18n( "" ), UserIcon( "" ) );
+
+ adjustSize();
+}
+
+KomparePrefDlg::~KomparePrefDlg()
+{
+
+}
+
+/** No descriptions */
+void KomparePrefDlg::slotDefault()
+{
+ kdDebug(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()
+{
+ // show some help...
+ // figure out the current active page
+ // and give help for that page
+}
+
+/** No descriptions */
+void KomparePrefDlg::slotApply()
+{
+ kdDebug(8103) << "SlotApply called -> Settings should be applied..." << endl;
+ // well apply the settings that are currently selected
+ m_viewPage->apply();
+ m_diffPage->apply();
+
+ emit applyClicked();
+}
+
+/** No descriptions */
+void KomparePrefDlg::slotOk()
+{
+ kdDebug(8103) << "SlotOk called -> Settings should be applied..." << endl;
+ // Apply the settings that are currently selected
+ m_viewPage->apply();
+ m_diffPage->apply();
+
+ KDialogBase::slotOk();
+}
+
+/** No descriptions */
+void KomparePrefDlg::slotCancel()
+{
+ // discard the current settings and use the present ones
+ m_viewPage->restore();
+ m_diffPage->restore();
+
+ KDialogBase::slotCancel();
+}
+
+#include "kompareprefdlg.moc"
diff --git a/kompare/komparepart/kompareprefdlg.h b/kompare/komparepart/kompareprefdlg.h
new file mode 100644
index 00000000..e81da90c
--- /dev/null
+++ b/kompare/komparepart/kompareprefdlg.h
@@ -0,0 +1,54 @@
+/***************************************************************************
+ kompareprefdlg.h - description
+ -------------------
+ begin : Sun Mar 4 2001
+ copyright : (C) 2001-2003 by Otto Bruggeman
+ and John Firebaugh
+ email : otto.bruggeman@home.nl
+ jfirebaugh@kde.org
+****************************************************************************/
+
+/***************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+***************************************************************************/
+
+#ifndef KOMPAREPREFDLG_H
+#define KOMPAREPREFDLG_H
+
+#include <kdialogbase.h>
+
+class DiffPage;
+class DiffSettings;
+class ViewPage;
+class ViewSettings;
+
+class KomparePrefDlg : public KDialogBase
+{
+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();
+
+private:
+ ViewPage* m_viewPage;
+ DiffPage* m_diffPage;
+};
+
+#endif
diff --git a/kompare/komparepart/komparesaveoptionsbase.ui b/kompare/komparepart/komparesaveoptionsbase.ui
new file mode 100644
index 00000000..4c49b018
--- /dev/null
+++ b/kompare/komparepart/komparesaveoptionsbase.ui
@@ -0,0 +1,336 @@
+<!DOCTYPE UI><UI version="3.0" stdsetdef="1">
+<class>KompareSaveOptionsBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>KompareSaveOptionsBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>558</width>
+ <height>399</height>
+ </rect>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QGroupBox" row="1" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>GroupBox2</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>1</hsizetype>
+ <vsizetype>1</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>Panel</enum>
+ </property>
+ <property name="title">
+ <string>Run Diff In</string>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="KURLRequester">
+ <property name="name">
+ <cstring>m_directoryRequester</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>7</hsizetype>
+ <vsizetype>5</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QGroupBox" row="2" column="0" rowspan="1" colspan="2">
+ <property name="name">
+ <cstring>m_CommandLineGB</cstring>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy>
+ <hsizetype>3</hsizetype>
+ <vsizetype>3</vsizetype>
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="frameShape">
+ <enum>Panel</enum>
+ </property>
+ <property name="title">
+ <string>Command Line</string>
+ </property>
+ <property name="alignment">
+ <set>AlignVCenter|AlignLeft</set>
+ </property>
+ <property name="hAlign" stdset="0">
+ </property>
+ <property name="vAlign" stdset="0">
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>m_CommandLineLabel</cstring>
+ </property>
+ <property name="text">
+ <string>cd dir &amp;&amp; diff -udHprNa -- source destination</string>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ <widget class="QButtonGroup" row="0" column="1">
+ <property name="name">
+ <cstring>m_OptionsGB</cstring>
+ </property>
+ <property name="title">
+ <string>Options</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>m_SmallerChangesCB</cstring>
+ </property>
+ <property name="text">
+ <string>Look for smaller changes</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>m_LargeFilesCB</cstring>
+ </property>
+ <property name="text">
+ <string>Optimize for large files</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>m_IgnoreCaseCB</cstring>
+ </property>
+ <property name="text">
+ <string>Ignore changes in case</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>m_ExpandTabsCB</cstring>
+ </property>
+ <property name="text">
+ <string>Expand tabs to spaces</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>m_IgnoreEmptyLinesCB</cstring>
+ </property>
+ <property name="text">
+ <string>Ignore added or removed empty lines</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>m_IgnoreWhiteSpaceCB</cstring>
+ </property>
+ <property name="text">
+ <string>Ignore changes in whitespace</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>m_FunctionNamesCB</cstring>
+ </property>
+ <property name="text">
+ <string>Show function names</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>m_RecursiveCB</cstring>
+ </property>
+ <property name="text">
+ <string>Compare folders recursively</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>m_NewFilesCB</cstring>
+ </property>
+ <property name="text">
+ <string>Treat new files as empty</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ <property name="tristate">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </vbox>
+ </widget>
+ <widget class="QButtonGroup" row="0" column="0">
+ <property name="name">
+ <cstring>m_FormatBG</cstring>
+ </property>
+ <property name="title">
+ <string>Format</string>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>11</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>m_ContextRB</cstring>
+ </property>
+ <property name="text">
+ <string>Context</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>m_EdRB</cstring>
+ </property>
+ <property name="text">
+ <string>Ed</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>m_NormalRB</cstring>
+ </property>
+ <property name="text">
+ <string>Normal</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>m_RCSRB</cstring>
+ </property>
+ <property name="text">
+ <string>RCS</string>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>m_UnifiedRB</cstring>
+ </property>
+ <property name="text">
+ <string>Unified</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <widget class="QRadioButton">
+ <property name="name">
+ <cstring>m_SideBySideRB</cstring>
+ </property>
+ <property name="text">
+ <string>Side-by-side</string>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ </widget>
+ <widget class="QLayoutWidget">
+ <property name="name">
+ <cstring>m_ContextLinesLayout</cstring>
+ </property>
+ <hbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <widget class="QLabel">
+ <property name="name">
+ <cstring>m_ContextLinesLabel</cstring>
+ </property>
+ <property name="text">
+ <string>Number of context lines:</string>
+ </property>
+ </widget>
+ <widget class="QSpinBox">
+ <property name="name">
+ <cstring>m_ContextLinesSB</cstring>
+ </property>
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="maxValue">
+ <number>65535</number>
+ </property>
+ <property name="value">
+ <number>3</number>
+ </property>
+ </widget>
+ </hbox>
+ </widget>
+ </vbox>
+ </widget>
+ </grid>
+</widget>
+<layoutdefaults spacing="6" margin="11"/>
+</UI>
diff --git a/kompare/komparepart/komparesaveoptionswidget.cpp b/kompare/komparepart/komparesaveoptionswidget.cpp
new file mode 100644
index 00000000..866ecc0d
--- /dev/null
+++ b/kompare/komparepart/komparesaveoptionswidget.cpp
@@ -0,0 +1,215 @@
+/***************************************************************************
+ komparesaveoptionswidget.cpp - description
+ -------------------
+ begin : Sun Mar 4 2001
+ copyright : (C) 2001-2003 by Otto Bruggeman
+ and John Firebaugh
+ email : otto.bruggeman@home.nl
+ jfirebaugh@kde.org
+****************************************************************************/
+
+/***************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+***************************************************************************/
+
+#include <qbuttongroup.h>
+#include <qcheckbox.h>
+#include <qlabel.h>
+#include <qradiobutton.h>
+#include <qspinbox.h>
+
+#include <kdebug.h>
+#include <kfiledialog.h>
+#include <kurlrequester.h>
+
+#include "diffsettings.h"
+#include "komparesaveoptionswidget.h"
+
+KompareSaveOptionsWidget::KompareSaveOptionsWidget( QString source, QString destination,
+ DiffSettings * settings, QWidget * parent )
+ : KompareSaveOptionsBase( parent, "save options" )
+ , m_source( source )
+ , m_destination( destination )
+{
+ m_settings = settings;
+
+ m_directoryRequester->setMode( static_cast<KFile::Mode>(
+ 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();
+ }
+
+ // If we found a common root, change to that directory and
+ // strip the common part from source and destination.
+ if( root.isValid() ) {
+ 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()) );
+
+ loadOptions();
+}
+
+KompareSaveOptionsWidget::~KompareSaveOptionsWidget()
+{
+
+}
+
+QString KompareSaveOptionsWidget::directory() const
+{
+ return KURL( m_directoryRequester->url() ).path();
+}
+
+void KompareSaveOptionsWidget::updateCommandLine()
+{
+ QString cmdLine = "diff";
+
+ QString options = "";
+
+ switch( static_cast<Kompare::Format>( m_FormatBG->id( m_FormatBG->selected() ) ) ) {
+ 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(), m_source );
+ cmdLine += " ";
+ cmdLine += constructRelativePath( m_directoryRequester->url(), 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->setButton ( m_settings->m_format );
+
+ 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<Kompare::Format>( m_FormatBG->id( m_FormatBG->selected() ) );
+
+}
+
+#include "komparesaveoptionswidget.moc"
diff --git a/kompare/komparepart/komparesaveoptionswidget.h b/kompare/komparepart/komparesaveoptionswidget.h
new file mode 100644
index 00000000..6361ca77
--- /dev/null
+++ b/kompare/komparepart/komparesaveoptionswidget.h
@@ -0,0 +1,51 @@
+/***************************************************************************
+ komparesaveoptionswidget.h - description
+ -------------------
+ begin : Sun Mar 4 2001
+ copyright : (C) 2001-2003 by Otto Bruggeman
+ and John Firebaugh
+ email : otto.bruggeman@home.nl
+ jfirebaugh@kde.org
+****************************************************************************/
+
+/***************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+***************************************************************************/
+
+#ifndef KOMPARESAVEOPTIONSWIDGET_H
+#define KOMPARESAVEOPTIONSWIDGET_H
+
+#include <kurl.h>
+
+#include "komparesaveoptionsbase.h"
+#include "kompare.h"
+
+class DiffSettings;
+
+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;
+};
+
+#endif
diff --git a/kompare/komparepart/komparesplitter.cpp b/kompare/komparepart/komparesplitter.cpp
new file mode 100644
index 00000000..fcf014cb
--- /dev/null
+++ b/kompare/komparepart/komparesplitter.cpp
@@ -0,0 +1,712 @@
+/***************************************************************************
+ komparesplitter.cpp - description
+ -------------------
+ begin : Wed Jan 14 2004
+ copyright : (C) 2004 by Jeff Snyder
+ email : jeff@caffeinated.me.uk
+****************************************************************************/
+
+/***************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the 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 "komparesplitter.h"
+
+#include <qstyle.h>
+#include <qstring.h>
+#include <qtimer.h>
+
+#include <kdebug.h>
+#include <kapplication.h>
+#include <kglobalsettings.h>
+
+#include "diffmodel.h"
+#include "difference.h"
+#include "viewsettings.h"
+#include "kompare_part.h"
+#include "komparelistview.h"
+#include "kompareconnectwidget.h"
+
+using namespace Diff2;
+
+KompareSplitter::KompareSplitter( ViewSettings *settings, QWidget * parent,
+ const char *name) :
+ QSplitter( Horizontal, parent, name ),
+ m_settings( settings )
+{
+ QFrame *scrollFrame = new QFrame( parent, "scrollFrame" );
+ reparent( scrollFrame, *(new QPoint()), false );
+
+ // Set up the scrollFrame
+ scrollFrame->setFrameStyle( QFrame::NoFrame | QFrame::Plain );
+ scrollFrame->setLineWidth(scrollFrame->style().pixelMetric(QStyle::PM_DefaultFrameWidth));
+ QGridLayout *pairlayout = new QGridLayout(scrollFrame, 2, 2, 0);
+ m_vScroll = new QScrollBar( QScrollBar::Vertical, scrollFrame );
+ pairlayout->addWidget( m_vScroll, 0, 1 );
+ m_hScroll = new QScrollBar( QScrollBar::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
+ setFrameStyle( QFrame::StyledPanel | QFrame::Sunken );
+ setLineWidth( style().pixelMetric( QStyle::PM_DefaultFrameWidth ) );
+
+ setHandleWidth(50);
+ setChildrenCollapsible( false );
+ setFrameStyle( QFrame::NoFrame );
+ setSizePolicy( QSizePolicy (QSizePolicy::Ignored, QSizePolicy::Ignored ));
+ setOpaqueResize( true );
+ setFocusPolicy( QWidget::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(scrollToId(int)) );
+ connect( m_vScroll, SIGNAL(sliderMoved(int)), SLOT(scrollToId(int)) );
+ connect( m_hScroll, SIGNAL(valueChanged(int)), SIGNAL(setXOffset(int)) );
+ connect( m_hScroll, SIGNAL(sliderMoved(int)), SIGNAL(setXOffset(int)) );
+
+ m_scrollTimer=new QTimer();
+ 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::ChildInserted);
+
+ // init stuff
+ slotUpdateScrollBars();
+}
+
+KompareSplitter::~KompareSplitter(){}
+
+/*
+ Inserts the widget \a w at the end (or at the beginning if \a
+ prepend is TRUE) of the splitter's list of widgets.
+
+ It is the responsibility of the caller to make sure that \a w is
+ not already in the splitter and to call recalcId() if needed. (If
+ \a prepend is TRUE, then recalcId() is very probably needed.)
+*/
+
+QSplitterLayoutStruct *KompareSplitter::addWidget( KompareListViewFrame *w, bool prepend )
+{
+ /* This function is *not* a good place to make connections to and from
+ * the KompareListView. Make them in the KompareListViewFrame constructor
+ * instad - that way the connections get made upon creation, not upon the
+ * next processing of the event queue. */
+
+ QSplitterLayoutStruct *s;
+ KompareConnectWidgetFrame *newHandle = 0;
+ if ( d->list.count() > 0 ) {
+ s = new QSplitterLayoutStruct;
+ s->resizeMode = KeepSize;
+ QString tmp = "qt_splithandle_";
+ tmp += w->name();
+ KompareListView *lw =
+ ((KompareListViewFrame*)(prepend?w:d->list.last()->wid))->view();
+ KompareListView *rw =
+ ((KompareListViewFrame*)(prepend?d->list.first()->wid:w))->view();
+ newHandle = new KompareConnectWidgetFrame(lw, rw, m_settings, this, tmp.latin1());
+ s->wid = newHandle;
+ newHandle->setId( d->list.count() );
+ s->isHandle = TRUE;
+ s->sizer = pick( newHandle->sizeHint() );
+ if ( prepend )
+ d->list.prepend( s );
+ else
+ d->list.append( s );
+ }
+ s = new QSplitterLayoutStruct;
+ s->resizeMode = DefaultResizeMode;
+ s->wid = w;
+ s->isHandle = FALSE;
+ if ( prepend ) d->list.prepend( s );
+ else d->list.append( s );
+ if ( newHandle && isVisible() )
+ newHandle->show(); // will trigger sending of post events
+ return s;
+}
+
+
+/*!
+ Tells the splitter that the child widget described by \a c has
+ been inserted or removed.
+*/
+
+void KompareSplitter::childEvent( QChildEvent *c )
+{
+ if ( c->type() == QEvent::ChildInserted ) {
+ if ( !c->child()->isWidgetType() )
+ return;
+
+ if ( ((QWidget*)c->child())->testWFlags( WType_TopLevel ) )
+ return;
+
+ QSplitterLayoutStruct *s = d->list.first();
+ while ( s ) {
+ if ( s->wid == c->child() )
+ return;
+ s = d->list.next();
+ }
+ addWidget( (KompareListViewFrame*)c->child() );
+ recalc( isVisible() );
+ } else if ( c->type() == QEvent::ChildRemoved ) {
+ QSplitterLayoutStruct *prev = 0;
+ if ( d->list.count() > 1 )
+ prev = d->list.at( 1 ); // yes, this is correct
+ QSplitterLayoutStruct *curr = d->list.first();
+ while ( curr ) {
+ if ( curr->wid == c->child() ) {
+ d->list.removeRef( curr );
+ if ( prev && prev->isHandle ) {
+ QWidget *w = prev->wid;
+ d->list.removeRef( prev );
+ delete w; // will call childEvent()
+ }
+ recalcId();
+ doResize();
+ return;
+ }
+ prev = curr;
+ curr = d->list.next();
+ }
+ }
+}
+
+// This is from a private qt header (kernel/qlayoutengine_p.h). sorry.
+QSize qSmartMinSize( QWidget *w );
+
+static QPoint toggle( QWidget *w, QPoint pos )
+{
+ QSize minS = qSmartMinSize( w );
+ return -pos - QPoint( minS.width(), minS.height() );
+}
+
+static bool isCollapsed( QWidget *w )
+{
+ return w->x() < 0 || w->y() < 0;
+}
+
+static QPoint topLeft( QWidget *w )
+{
+ if ( isCollapsed(w) ) {
+ return toggle( w, w->pos() );
+ } else {
+ return w->pos();
+ }
+}
+
+static QPoint bottomRight( QWidget *w )
+{
+ if ( isCollapsed(w) ) {
+ return toggle( w, w->pos() ) - QPoint( 1, 1 );
+ } else {
+ return w->geometry().bottomRight();
+ }
+}
+
+void KompareSplitter::moveSplitter( QCOORD p, int id )
+{
+ QSplitterLayoutStruct *s = d->list.at( id );
+ int farMin;
+ int min;
+ int max;
+ int farMax;
+ p = adjustPos( p, id, &farMin, &min, &max, &farMax );
+ int oldP = pick( s->wid->pos() );
+ int* poss = new int[d->list.count()];
+ int* ws = new int[d->list.count()];
+ QWidget *w = 0;
+ bool upLeft;
+ if ( QApplication::reverseLayout() && orient == Horizontal ) {
+ int q = p + s->wid->width();
+ doMove( FALSE, q, id - 1, -1, (p > max), poss, ws );
+ doMove( TRUE, q, id, -1, (p < min), poss, ws );
+ upLeft = (q > oldP);
+ } else {
+ doMove( FALSE, p, id, +1, (p > max), poss, ws );
+ doMove( TRUE, p, id - 1, +1, (p < min), poss, ws );
+ upLeft = (p < oldP);
+ }
+ if ( upLeft ) {
+ int count = d->list.count();
+ for ( int id = 0; id < count; ++id ) {
+ w = d->list.at( id )->wid;
+ if( !w->isHidden() )
+ setGeo( w, poss[id], ws[id], TRUE );
+ }
+ } else {
+ for ( int id = d->list.count() - 1; id >= 0; --id ) {
+ w = d->list.at( id )->wid;
+ if( !w->isHidden() )
+ setGeo( w, poss[id], ws[id], TRUE );
+ }
+ }
+ storeSizes();
+}
+
+void KompareSplitter::doMove( bool backwards, int pos, int id, int delta,
+ bool mayCollapse, int* positions, int* widths )
+{
+ QSplitterLayoutStruct *s;
+ QWidget *w;
+ for ( ; id >= 0 && id < (int)d->list.count();
+ id = backwards ? id - delta : id + delta ) {
+ s = d->list.at( id );
+ w = s->wid;
+ if ( w->isHidden() ) {
+ mayCollapse = TRUE;
+ } else {
+ if ( s->isHandle ) {
+ int dd = s->getSizer( orient );
+ int nextPos = backwards ? pos - dd : pos + dd;
+ positions[id] = pos;
+ widths[id] = dd;
+ pos = nextPos;
+ } else {
+ int dd = backwards ? pos - pick( topLeft(w) )
+ : pick( bottomRight(w) ) - pos + 1;
+ if ( dd > 0 || (!isCollapsed(w) && !mayCollapse) ) {
+ dd = QMAX( pick(qSmartMinSize(w)),
+ QMIN(dd, pick(w->maximumSize())) );
+ } else {
+ dd = 0;
+ }
+ positions[id] = backwards ? pos - dd : pos;
+ widths[id] = dd;
+ pos = backwards ? pos - dd : pos + dd;
+ mayCollapse = TRUE;
+ }
+ }
+ }
+}
+
+void KompareSplitter::slotSetSelection( const DiffModel* model, const Difference* diff )
+{
+ QSplitterLayoutStruct *curr;
+ for ( curr = d->list.first(); curr; curr = d->list.next() )
+ {
+ if ( curr->isHandle )
+ ((KompareConnectWidgetFrame*)
+ curr->wid)->wid()->slotSetSelection( model, diff );
+ else
+ {
+ ((KompareListViewFrame*)
+ curr->wid)->view()->slotSetSelection( model, diff );
+ ((KompareListViewFrame*)
+ curr->wid)->slotSetModel( model );
+ }
+ }
+ slotDelayedRepaintHandles();
+ slotDelayedUpdateScrollBars();
+}
+
+void KompareSplitter::slotSetSelection( const Difference* diff )
+{
+ QSplitterLayoutStruct *curr;
+ for ( curr = d->list.first(); curr; curr = d->list.next() )
+ if ( curr->isHandle )
+ ((KompareConnectWidgetFrame*)
+ curr->wid)->wid()->slotSetSelection( diff );
+ else
+ ((KompareListViewFrame*)
+ curr->wid)->view()->slotSetSelection( diff );
+ slotDelayedRepaintHandles();
+ slotDelayedUpdateScrollBars();
+}
+
+void KompareSplitter::slotApplyDifference( bool apply )
+{
+ QSplitterLayoutStruct *curr;
+ for ( curr = d->list.first(); curr; curr = d->list.next() )
+ if ( !curr->isHandle )
+ ((KompareListViewFrame*)
+ curr->wid)->view()->slotApplyDifference( apply );
+ slotDelayedRepaintHandles();
+}
+
+void KompareSplitter::slotApplyAllDifferences( bool apply )
+{
+ QSplitterLayoutStruct *curr;
+ for ( curr = d->list.first(); curr; curr = d->list.next() )
+ if ( !curr->isHandle )
+ ((KompareListViewFrame*)
+ curr->wid)->view()->slotApplyAllDifferences( apply );
+ slotDelayedRepaintHandles();
+ scrollToId( scrollTo ); // FIXME!
+}
+
+void KompareSplitter::slotApplyDifference( const Difference* diff, bool apply )
+{
+ QSplitterLayoutStruct *curr;
+ for ( curr = d->list.first(); curr; curr = d->list.next() )
+ if ( !curr->isHandle )
+ ((KompareListViewFrame*)
+ curr->wid)->view()->slotApplyDifference( diff, apply );
+ slotDelayedRepaintHandles();
+}
+
+void KompareSplitter::slotDifferenceClicked( const Difference* diff )
+{
+ QSplitterLayoutStruct *curr;
+ for ( curr = d->list.first(); curr; curr = d->list.next() )
+ if ( !curr->isHandle )
+ ((KompareListViewFrame*)
+ curr->wid)->view()->setSelectedDifference( diff, false );
+ emit selectionChanged( diff );
+}
+
+void KompareSplitter::slotConfigChanged()
+{
+ QSplitterLayoutStruct *curr;
+ for ( curr = d->list.first(); curr; curr = d->list.next() ) {
+ if ( !curr->isHandle )
+ {
+ ((KompareListViewFrame*)
+ curr->wid)->view()->setSpaces( m_settings->m_tabToNumberOfSpaces );
+ ((KompareListViewFrame*)
+ curr->wid)->view()->setFont( m_settings->m_font );
+ ((KompareListViewFrame*)
+ curr->wid)->view()->update();
+ }
+ }
+}
+
+void KompareSplitter::slotDelayedRepaintHandles()
+{
+ QSplitterLayoutStruct *curr;
+ for ( curr = d->list.first(); curr; curr = d->list.next() )
+ if ( curr->isHandle )
+ ((KompareConnectWidgetFrame*)curr->wid)->wid()->slotDelayedRepaint();
+}
+
+void KompareSplitter::repaintHandles()
+{
+ QSplitterLayoutStruct *curr;
+ for ( curr = d->list.first(); curr; curr = d->list.next() )
+ if ( curr->isHandle )
+ ((KompareConnectWidgetFrame*)curr->wid)->wid()->repaint();
+}
+
+// Scrolling stuff
+/*
+ * limit updating to once every 50 msec with a qtimer
+ * FIXME: i'm a nasty hack
+ */
+
+void KompareSplitter::wheelEvent( QWheelEvent* e )
+{
+ // scroll lines...
+ if ( e->orientation() == Qt::Vertical )
+ {
+ if ( e->state() & Qt::ControlButton )
+ if ( e->delta() < 0 ) // scroll down one page
+ m_vScroll->addPage();
+ else // scroll up one page
+ m_vScroll->subtractPage();
+ else
+ if ( e->delta() < 0 ) // scroll down
+ m_vScroll->addLine();
+ else // scroll up
+ m_vScroll->subtractLine();
+ }
+ else
+ {
+ if ( e->state() & Qt::ControlButton )
+ if ( e->delta() < 0 ) // scroll right one page
+ m_hScroll->addPage();
+ else // scroll left one page
+ m_hScroll->subtractPage();
+ else
+ if ( e->delta() < 0 ) // scroll to the right
+ m_hScroll->addLine();
+ else // scroll to the left
+ m_hScroll->subtractLine();
+ }
+ e->accept();
+ repaintHandles();
+}
+
+void KompareSplitter::keyPressEvent( QKeyEvent* e )
+{
+ //keyboard scrolling
+ switch ( e->key() ) {
+ case Key_Right:
+ case Key_L:
+ m_hScroll->addLine();
+ break;
+ case Key_Left:
+ case Key_H:
+ m_hScroll->subtractLine();
+ break;
+ case Key_Up:
+ case Key_K:
+ m_vScroll->subtractLine();
+ break;
+ case Key_Down:
+ case Key_J:
+ m_vScroll->addLine();
+ break;
+ case Key_PageDown:
+ m_vScroll->addPage();
+ break;
+ case Key_PageUp:
+ m_vScroll->subtractPage();
+ break;
+ }
+ e->accept();
+ repaintHandles();
+}
+
+void KompareSplitter::timerTimeout()
+{
+ if ( restartTimer )
+ restartTimer = false;
+ else
+ m_scrollTimer->stop();
+
+ slotDelayedRepaintHandles();
+
+ emit scrollViewsToId( scrollTo );
+}
+
+void KompareSplitter::scrollToId( int id )
+{
+ scrollTo = id;
+
+ if( restartTimer )
+ return;
+
+ if( m_scrollTimer->isActive() )
+ {
+ restartTimer = true;
+ }
+ else
+ {
+ emit scrollViewsToId( id );
+ slotDelayedRepaintHandles();
+ m_scrollTimer->start(30, false);
+ }
+}
+
+void KompareSplitter::slotDelayedUpdateScrollBars()
+{
+ QTimer::singleShot( 0, this, SLOT( slotUpdateScrollBars() ) );
+}
+
+void KompareSplitter::slotUpdateScrollBars()
+{
+ int m_scrollDistance = m_settings->m_scrollNoOfLines * lineSpacing();
+ int m_pageSize = pageSize();
+
+ if( needVScrollBar() )
+ {
+ m_vScroll->show();
+
+ m_vScroll->blockSignals( true );
+ m_vScroll->setRange( minVScrollId(),
+ maxVScrollId() );
+ m_vScroll->setValue( scrollId() );
+ m_vScroll->setSteps( m_scrollDistance, m_pageSize );
+ m_vScroll->blockSignals( false );
+ }
+ else
+ {
+ m_vScroll->hide();
+ }
+
+ if( needHScrollBar() )
+ {
+ m_hScroll->show();
+ m_hScroll->blockSignals( true );
+ m_hScroll->setRange( 0, maxHScrollId() );
+ m_hScroll->setValue( maxContentsX() );
+ m_hScroll->setSteps( 10, 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() );
+}
+
+/* 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()
+{
+ QSplitterLayoutStruct *curr = d->list.first();
+ for ( curr = d->list.first(); curr; curr = d->list.next() )
+ if ( !curr->isHandle )
+ return ((KompareListViewFrame*) curr->wid)->view()->scrollId();
+ return minVScrollId();
+}
+
+int KompareSplitter::lineSpacing()
+{
+ QSplitterLayoutStruct *curr = d->list.first();
+ for ( curr = d->list.first(); curr; curr = d->list.next() )
+ if ( !curr->isHandle )
+ return ((KompareListViewFrame*)
+ curr->wid)->view()->fontMetrics().lineSpacing();
+ return 1;
+}
+
+int KompareSplitter::pageSize()
+{
+ QSplitterLayoutStruct *curr;
+ for ( curr = d->list.first(); curr; curr = d->list.next() )
+ {
+ if ( !curr->isHandle )
+ {
+ KompareListView *view = ((KompareListViewFrame*) curr->wid)->view();
+ return view->visibleHeight() - QStyle::PM_ScrollBarExtent;
+ }
+ }
+ return 1;
+}
+
+bool KompareSplitter::needVScrollBar()
+{
+ QSplitterLayoutStruct *curr;
+ int pagesize = pageSize();
+ for ( curr = d->list.first(); curr; curr = d->list.next() )
+ {
+ if( !curr->isHandle )
+ {
+ KompareListView *view = ((KompareListViewFrame*) curr->wid)->view();
+ if( view ->contentsHeight() > pagesize)
+ return true;
+ }
+ }
+ return false;
+}
+
+int KompareSplitter::minVScrollId()
+{
+
+ QSplitterLayoutStruct *curr;
+ int min = -1;
+ int mSId;
+ for ( curr = d->list.first(); curr; curr = d->list.next() )
+ {
+ if(!curr->isHandle) {
+ KompareListView* view = ((KompareListViewFrame*)curr->wid)->view();
+ mSId = view->minScrollId();
+ if (mSId < min || min == -1) min = mSId;
+ }
+ }
+ return ( min == -1 ) ? 0 : min;
+}
+
+int KompareSplitter::maxVScrollId()
+{
+ QSplitterLayoutStruct *curr;
+ int max = 0;
+ int mSId;
+ for ( curr = d->list.first(); curr; curr = d->list.next() )
+ {
+ if ( !curr->isHandle )
+ {
+ mSId = ((KompareListViewFrame*)curr->wid)->view()->maxScrollId();
+ if ( mSId > max )
+ max = mSId;
+ }
+ }
+ return max;
+}
+
+bool KompareSplitter::needHScrollBar()
+{
+ QSplitterLayoutStruct *curr;
+ for ( curr = d->list.first(); curr; curr = d->list.next() )
+ {
+ if( !curr->isHandle )
+ {
+ KompareListView *view = ((KompareListViewFrame*) curr->wid)->view();
+ if ( view->contentsWidth() > view->visibleWidth() )
+ return true;
+ }
+ }
+ return false;
+}
+
+int KompareSplitter::maxHScrollId()
+{
+ QSplitterLayoutStruct *curr;
+ int max = 0;
+ int mHSId;
+ for ( curr = d->list.first(); curr; curr = d->list.next() )
+ {
+ if( !curr->isHandle )
+ {
+ KompareListView *view = ((KompareListViewFrame*) curr->wid)->view();
+ mHSId = view->contentsWidth() - view->visibleWidth();
+ if ( mHSId > max )
+ max = mHSId;
+ }
+ }
+ return max;
+}
+
+int KompareSplitter::maxContentsX()
+{
+ QSplitterLayoutStruct *curr;
+ int max = 0;
+ int mCX;
+ for ( curr = d->list.first(); curr; curr = d->list.next() )
+ {
+ if ( !curr->isHandle )
+ {
+ mCX = ((KompareListViewFrame*) curr->wid)->view()->contentsX();
+ if ( mCX > max )
+ max = mCX;
+ }
+ }
+ return max;
+}
+
+int KompareSplitter::minVisibleWidth()
+{
+ // Why the hell do we want to know this?
+ // ah yes, its because we use it to set the "page size" for horiz. scrolling.
+ // despite the fact that *noone* 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
+ QSplitterLayoutStruct *curr;
+ int min = -1;
+ int vW;
+ for( curr = d->list.first(); curr; curr = d->list.next() )
+ {
+ if ( !curr->isHandle ) {
+ vW = ((KompareListViewFrame*)curr->wid)->view()->visibleWidth();
+ if ( vW < min || min == -1 )
+ min = vW;
+ }
+ }
+ return ( min == -1 ) ? 0 : min;
+}
+
+#include "komparesplitter.moc"
diff --git a/kompare/komparepart/komparesplitter.h b/kompare/komparepart/komparesplitter.h
new file mode 100644
index 00000000..47eee6bf
--- /dev/null
+++ b/kompare/komparepart/komparesplitter.h
@@ -0,0 +1,115 @@
+/***************************************************************************
+ kompareview.cpp - description
+ -------------------
+ begin : Wed Jan 14 2004
+ copyright : (C) 2004 by Jeff Snyder
+ email : jeff@caffeinated.me.uk
+****************************************************************************/
+
+/***************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the 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 "kompare_qsplitter.h"
+
+#include <qtimer.h>
+
+#include "komparelistview.h"
+#include "komparemodellist.h"
+
+class QSplitterLayoutStruct;
+class QTextStream;
+class QSplitterHandle;
+
+namespace Diff2 {
+class DiffModel;
+class Difference;
+}
+class ViewSettings;
+
+class KompareSplitter : public QSplitter
+{
+ Q_OBJECT
+
+public:
+ KompareSplitter(ViewSettings *settings, QWidget *parent=0, const char *name = 0);
+ ~KompareSplitter();
+
+signals:
+ void selectionChanged( const Diff2::Difference* diff );
+
+ void configChanged();
+
+ void scrollViewsToId( int id );
+ void setXOffset( int x );
+
+public slots:
+ void slotApplyDifference( bool apply );
+ void slotApplyAllDifferences( bool apply );
+ void slotApplyDifference( const Diff2::Difference* diff, bool apply );
+
+ // to update the list views
+ void slotSetSelection( const Diff2::DiffModel* model, const Diff2::Difference* diff );
+ void slotSetSelection( const Diff2::Difference* diff );
+
+ void slotDifferenceClicked( const Diff2::Difference* diff );
+
+ void slotConfigChanged();
+
+ void scrollToId( int id );
+ void slotDelayedUpdateScrollBars();
+ void slotUpdateScrollBars();
+ void slotDelayedUpdateVScrollValue();
+ void slotUpdateVScrollValue();
+
+protected:
+ void childEvent( QChildEvent * );
+ void wheelEvent( QWheelEvent* e );
+ void keyPressEvent( QKeyEvent* e );
+
+ void moveSplitter( QCOORD pos, int id );
+
+private slots:
+ void slotDelayedRepaintHandles();
+ void timerTimeout();
+
+private:
+ QSplitterLayoutStruct *addWidget(KompareListViewFrame *w,
+ bool prepend = FALSE );
+
+ void doMove( bool backwards, int pos, int id, int delta,
+ bool mayCollapse, int* positions, int* widths );
+
+ void repaintHandles();
+
+ QTimer* m_scrollTimer;
+ bool restartTimer;
+ int scrollTo;
+
+ // Scrollbars. all this just for the goddamn scrollbars. i hate them.
+ int scrollId();
+ int lineSpacing();
+ int pageSize();
+ bool needVScrollBar();
+ int minVScrollId();
+ int maxVScrollId();
+ bool needHScrollBar();
+ int maxHScrollId();
+ int maxContentsX();
+ int minVisibleWidth();
+
+ ViewSettings* m_settings;
+ QScrollBar* m_vScroll;
+ QScrollBar* m_hScroll;
+
+ friend class KompareConnectWidgetFrame;
+};
+#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 @@
+<!DOCTYPE kpartgui SYSTEM "kpartgui.dtd">
+<kpartgui name="kompare_shell" version="9">
+<MenuBar>
+ <Menu noMerge="1" name="file"><text>&amp;File</text>
+ <Action name="file_open"/>
+ <Action name="file_compare_files"/>
+ <Action name="file_blend_url"/>
+ <Separator/>
+ <Merge/>
+ <Separator/>
+ <Action name="file_quit"/>
+ </Menu>
+ <Merge/>
+ <Menu noMerge="1" name="settings"><text>&amp;Settings</text>
+ <Merge name="StandardToolBarMenuHandler" />
+ <Action name="options_show_toolbar"/>
+ <Action name="options_show_statusbar"/>
+ <Action name="options_show_text_view"/>
+ <Merge name="show_merge"/>
+ <Separator/>
+ <Action name="options_configure_keybinding"/>
+ <Action name="options_configure_toolbars"/>
+ <Merge name="configure_merge"/>
+ <Merge/>
+ </Menu>
+</MenuBar>
+<ToolBar noMerge="1" name="mainToolBar"><text>Main Toolbar</text>
+ <Action name="file_compare_files"/>
+ <Separator/>
+ <Merge/>
+ <Action name="help"/>
+</ToolBar>
+</kpartgui>
diff --git a/kompare/kompareurldialog.cpp b/kompare/kompareurldialog.cpp
new file mode 100644
index 00000000..4630cd18
--- /dev/null
+++ b/kompare/kompareurldialog.cpp
@@ -0,0 +1,143 @@
+/***************************************************************************
+ comparedialog.cpp - description
+ -------------------
+ begin : Sun Mar 4 2001
+ copyright : (C) 2001-2004 Otto Bruggeman
+ (C) 2001-2003 John Firebaugh
+ email : otto.bruggeman@home.nl
+ jfirebaugh@kde.org
+****************************************************************************/
+
+/***************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+***************************************************************************/
+
+#include <qvbox.h>
+
+#include <kapplication.h>
+#include <klocale.h>
+#include <kurlrequester.h>
+
+#include "diffpage.h"
+#include "diffsettings.h"
+#include "filespage.h"
+#include "filessettings.h"
+#include "viewpage.h"
+#include "viewsettings.h"
+
+#include "kompareurldialog.h"
+
+KompareURLDialog::KompareURLDialog( QWidget *parent, const char *name )
+ : KDialogBase( IconList, "", Ok|Cancel, Ok, parent, name )
+{
+ setIconListAllVisible(true);
+
+ KConfig* cfg = kapp->config();
+ QVBox* filesBox = addVBoxPage( i18n( "Files" ), i18n( "Here you can enter the files you want to compare." ) );
+ m_filesPage = new FilesPage( filesBox );
+ m_filesSettings = new FilesSettings( this );
+ m_filesSettings->loadSettings( cfg );
+ m_filesPage->setSettings( m_filesSettings );
+
+ QVBox* diffBox = addVBoxPage( i18n( "Diff" ), i18n( "Here you can change the options for comparing the files." ) );
+ m_diffPage = new DiffPage( diffBox );
+ m_diffSettings = new DiffSettings( this );
+ m_diffSettings->loadSettings( cfg );
+ m_diffPage->setSettings( m_diffSettings );
+
+ QVBox* viewBox = addVBoxPage( i18n( "Appearance" ), i18n( "Here you can change the options for the view." ) );
+ m_viewPage = new ViewPage( viewBox );
+ m_viewSettings = new ViewSettings( this );
+ m_viewSettings->loadSettings( cfg );
+ m_viewPage->setSettings( m_viewSettings );
+
+ adjustSize();
+
+ enableButtonSeparator( true );
+
+ connect( m_filesPage->firstURLRequester(), SIGNAL( textChanged( const QString& ) ),
+ this, SLOT( slotEnableOk() ) );
+ connect( m_filesPage->secondURLRequester(), SIGNAL( textChanged( const QString& ) ),
+ this, SLOT( slotEnableOk() ) );
+
+ slotEnableOk();
+}
+
+KompareURLDialog::~KompareURLDialog()
+{
+}
+
+void KompareURLDialog::slotOk()
+{
+ m_filesPage->setURLsInComboBoxes();
+
+ KConfig* cfg = kapp->config();
+
+ m_filesPage->apply();
+ m_diffPage->apply();
+ m_viewPage->apply();
+
+ m_filesSettings->saveSettings( cfg );
+ m_diffSettings->saveSettings( cfg );
+ m_viewSettings->saveSettings( cfg );
+
+ cfg->sync();
+
+ KDialogBase::slotOk();
+}
+
+void KompareURLDialog::slotEnableOk()
+{
+ enableButtonOK( !m_filesPage->firstURLRequester()->url().isEmpty() &&
+ !m_filesPage->secondURLRequester()->url().isEmpty() );
+}
+
+KURL KompareURLDialog::getFirstURL() const
+{
+ return KURL( m_filesPage->firstURLRequester()->url() );
+}
+
+KURL KompareURLDialog::getSecondURL() const
+{
+ return KURL( m_filesPage->secondURLRequester()->url() );
+}
+
+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( kapp->config() );
+ 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..61db9974
--- /dev/null
+++ b/kompare/kompareurldialog.h
@@ -0,0 +1,76 @@
+/***************************************************************************
+ kcompareurldialog.h - description
+ -------------------
+ begin : Sun Mar 4 2001
+ copyright : (C) 2001-2004 Otto Bruggeman
+ (C) 2001-2003 John Firebaugh
+ email : otto.bruggeman@home.nl
+ jfirebaugh@kde.org
+****************************************************************************/
+
+/***************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+***************************************************************************/
+
+#ifndef KOMPAREURLDIALOG_H
+#define KOMPAREURLDIALOG_H
+
+#include <kdialogbase.h>
+#include <kurl.h>
+
+class QGroupBox;
+
+class KComboBox;
+class KConfig;
+class KFileDialog;
+class KURLComboBox;
+class KURLRequester;
+
+class FilesPage;
+class FilesSettings;
+class DiffPage;
+class DiffSettings;
+class ViewPage;
+class ViewSettings;
+
+class KompareURLDialog : public KDialogBase
+{
+ Q_OBJECT
+
+public:
+ KompareURLDialog( QWidget* parent = 0, const char* name = 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 slotOk();
+
+private slots:
+ void slotEnableOk();
+
+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/Makefile.am b/kompare/libdialogpages/Makefile.am
new file mode 100644
index 00000000..e13405eb
--- /dev/null
+++ b/kompare/libdialogpages/Makefile.am
@@ -0,0 +1,32 @@
+INCLUDES = \
+ -I$(top_srcdir)/kompare/libdiff2 \
+ $(all_includes)
+
+noinst_HEADERS = \
+ settingsbase.h \
+ diffsettings.h \
+ filessettings.h \
+ viewsettings.h \
+ pagebase.h \
+ diffpage.h \
+ filespage.h \
+ viewpage.h
+
+# let automoc handle all of the meta source files (moc)
+METASOURCES = AUTO
+
+noinst_LTLIBRARIES = libdialogpages.la
+
+# the library's source, library search path, and link libraries
+libdialogpages_la_SOURCES = \
+ settingsbase.cpp \
+ diffsettings.cpp \
+ filessettings.cpp \
+ viewsettings.cpp \
+ pagebase.cpp \
+ diffpage.cpp \
+ filespage.cpp \
+ viewpage.cpp
+
+libdialogpages_la_LDFLAGS = $(all_libraries)
+libdialogpages_la_LIBADD = $(LIB_KFILE)
diff --git a/kompare/libdialogpages/diffpage.cpp b/kompare/libdialogpages/diffpage.cpp
new file mode 100644
index 00000000..7f805c5b
--- /dev/null
+++ b/kompare/libdialogpages/diffpage.cpp
@@ -0,0 +1,355 @@
+/***************************************************************************
+ diffprefs.cpp - description
+ -------------------
+ begin : Sun Mar 4 2001
+ copyright : (C) 2001-2004 Otto Bruggeman
+ email : otto.bruggeman@home.nl
+****************************************************************************/
+
+/***************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the 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 <qcheckbox.h>
+#include <qhgroupbox.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qradiobutton.h>
+#include <qspinbox.h>
+#include <qtooltip.h>
+#include <qvbuttongroup.h>
+#include <qwhatsthis.h>
+
+#include <kapplication.h>
+#include <kcombobox.h>
+#include <kdialog.h>
+#include <keditlistbox.h>
+#include <klineedit.h>
+#include <klocale.h>
+#include <ktrader.h>
+#include <kurlcombobox.h>
+#include <kurlrequester.h>
+
+#include <kparts/componentfactory.h>
+#include <kregexpeditorinterface.h>
+
+#include "diffsettings.h"
+
+#include "diffpage.h"
+
+DiffPage::DiffPage( QWidget* parent ) : PageBase( parent ),
+ m_ignoreRegExpDialog( 0 )
+{
+ 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_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->setButton( m_settings->m_format );
+
+ m_excludeFilePatternCheckBox->setChecked ( m_settings->m_excludeFilePattern );
+ slotExcludeFilePatternToggled ( m_settings->m_excludeFilePattern );
+ m_excludeFilePatternEditListBox->insertStringList( m_settings->m_excludeFilePatternList );
+
+ m_excludeFileCheckBox->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();
+
+ 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<Kompare::Format>( m_modeButtonGroup->selectedId() );
+
+ m_settings->m_excludeFilePattern = m_excludeFilePatternCheckBox->isChecked();
+ m_settings->m_excludeFilePatternList = m_excludeFilePatternEditListBox->items();
+
+ m_settings->m_excludeFilesFile = m_excludeFileCheckBox->isChecked();
+ m_settings->m_excludeFilesFileURL = m_excludeFileURLComboBox->currentText();
+ m_settings->m_excludeFilesFileHistoryList = m_excludeFileURLComboBox->urls();
+
+ m_settings->saveSettings( kapp->config() );
+}
+
+void DiffPage::setDefaults()
+{
+ m_diffURLRequester->setURL( "diff" );
+ 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::null );
+
+ m_locSpinBox->setValue( 3 );
+
+ m_modeButtonGroup->setButton( Kompare::Unified );
+
+ m_excludeFilePatternCheckBox->setChecked( false );
+
+ m_excludeFileCheckBox->setChecked( false );
+}
+
+void DiffPage::slotShowRegExpEditor()
+{
+ if ( ! m_ignoreRegExpDialog )
+ m_ignoreRegExpDialog = KParts::ComponentFactory::createInstanceFromQuery<QDialog>( "KRegExpEditor/KRegExpEditor", QString::null, this );
+
+ KRegExpEditorInterface *iface = static_cast<KRegExpEditorInterface *>( m_ignoreRegExpDialog->qt_cast( "KRegExpEditorInterface" ) );
+
+ 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 )
+{
+ if ( !on )
+ {
+ m_excludeFilePatternEditListBox->setEnabled( false );
+ }
+ else
+ {
+ m_excludeFilePatternEditListBox->setEnabled( true );
+ }
+}
+
+void DiffPage::slotExcludeFileToggled( bool on )
+{
+ if ( !on )
+ {
+ m_excludeFileURLComboBox->setEnabled( false );
+ m_excludeFileURLRequester->setEnabled( false );
+ }
+ else
+ {
+ m_excludeFileURLComboBox->setEnabled( true );
+ m_excludeFileURLRequester->setEnabled( true );
+ }
+}
+
+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 QVButtonGroup( i18n( "Diff Program" ), page );
+ layout->addWidget( m_diffProgramGroup );
+ m_diffProgramGroup->setMargin( KDialog::marginHint() );
+
+ m_diffURLRequester = new KURLRequester( m_diffProgramGroup, "diffURLRequester" );
+ QWhatsThis::add( m_diffURLRequester, 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." ) );
+
+ layout->addStretch( 1 );
+ page->setMinimumSize( sizeHintForWidget( page ) );
+
+ 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 QVButtonGroup( i18n( "Output Format" ), page );
+ QWhatsThis::add( m_modeButtonGroup, 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( m_modeButtonGroup );
+ m_modeButtonGroup->setMargin( KDialog::marginHint() );
+
+ QRadioButton* radioButton;
+ radioButton = new QRadioButton( i18n( "Context" ), m_modeButtonGroup );
+ radioButton = new QRadioButton( i18n( "Ed" ), m_modeButtonGroup );
+ radioButton->setEnabled( false );
+ radioButton = new QRadioButton( i18n( "Normal" ), m_modeButtonGroup );
+ radioButton = new QRadioButton( i18n( "RCS" ), m_modeButtonGroup );
+ radioButton->setEnabled( false );
+ radioButton = new QRadioButton( i18n( "Unified" ), m_modeButtonGroup );
+
+ // #lines of context (loc)
+ QHGroupBox* groupBox = new QHGroupBox( i18n( "Lines of Context" ), page );
+ layout->addWidget( groupBox );
+ groupBox->setMargin( KDialog::marginHint() );
+
+ QLabel* label = new QLabel( i18n( "Number of context lines:" ), groupBox );
+ m_locSpinBox = new QSpinBox( 0, 100, 1, groupBox );
+ QWhatsThis::add( m_locSpinBox, 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 ) );
+
+ 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
+ QVButtonGroup* optionButtonGroup = new QVButtonGroup( i18n( "General" ), page );
+ layout->addWidget( optionButtonGroup );
+ optionButtonGroup->setMargin( KDialog::marginHint() );
+
+ m_smallerCheckBox = new QCheckBox( i18n( "&Look for smaller changes" ), optionButtonGroup );
+ QToolTip::add( m_smallerCheckBox, i18n( "This corresponds to the -d diff option." ) );
+ m_largerCheckBox = new QCheckBox( i18n( "O&ptimize for large files" ), optionButtonGroup );
+ QToolTip::add( m_largerCheckBox, i18n( "This corresponds to the -H diff option." ) );
+ m_caseCheckBox = new QCheckBox( i18n( "&Ignore changes in case" ), optionButtonGroup );
+ QToolTip::add( m_caseCheckBox, i18n( "This corresponds to the -i diff option." ) );
+
+ QHBoxLayout* groupLayout = new QHBoxLayout( layout, -1, "regexp_horizontal_layout" );
+ groupLayout->setMargin( KDialog::marginHint() );
+
+ m_ignoreRegExpCheckBox = new QCheckBox( i18n( "Ignore regexp:" ), page );
+ QToolTip::add( m_ignoreRegExpCheckBox, i18n( "This option corresponds to the -I diff option." ) );
+ groupLayout->addWidget( m_ignoreRegExpCheckBox );
+ m_ignoreRegExpEdit = new KLineEdit( QString::null, page, "regexplineedit" );
+ QToolTip::add( m_ignoreRegExpEdit, i18n( "Add the regular expression here that you want to use\nto ignore lines that match it." ) );
+ groupLayout->addWidget( m_ignoreRegExpEdit );
+
+ if ( !KTrader::self()->query("KRegExpEditor/KRegExpEditor").isEmpty() )
+ {
+ // Ok editor is available, use it
+ QButton* ignoreRegExpEditButton = new QPushButton( i18n( "&Edit..." ), page, "regexp_editor_button" );
+ QToolTip::add( ignoreRegExpEditButton, 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() ) );
+ }
+
+ QVButtonGroup* moreOptionButtonGroup = new QVButtonGroup( i18n( "Whitespace" ), page );
+ layout->addWidget( moreOptionButtonGroup );
+ moreOptionButtonGroup->setMargin( KDialog::marginHint() );
+
+ m_tabsCheckBox = new QCheckBox( i18n( "E&xpand tabs to spaces in output" ), moreOptionButtonGroup );
+ QToolTip::add( m_tabsCheckBox, i18n( "This option corresponds to the -t diff option." ) );
+ m_linesCheckBox = new QCheckBox( i18n( "I&gnore added or removed empty lines" ), moreOptionButtonGroup );
+ QToolTip::add( m_linesCheckBox, i18n( "This option corresponds to the -B diff option." ) );
+ m_whitespaceCheckBox = new QCheckBox( i18n( "Ig&nore changes in the amount of whitespace" ), moreOptionButtonGroup );
+ QToolTip::add( m_whitespaceCheckBox, i18n( "This option corresponds to the -b diff option." ) );
+ m_allWhitespaceCheckBox = new QCheckBox( i18n( "Ign&ore all whitespace" ), moreOptionButtonGroup );
+ QToolTip::add( m_allWhitespaceCheckBox, i18n( "This option corresponds to the -w diff option." ) );
+ m_ignoreTabExpansionCheckBox = new QCheckBox( i18n( "Igno&re changes due to tab expansion" ), moreOptionButtonGroup );
+ QToolTip::add( m_ignoreTabExpansionCheckBox, i18n( "This option corresponds to the -E diff option." ) );
+
+ layout->addStretch( 1 );
+ page->setMinimumSize( sizeHintForWidget( page ) );
+
+ addTab( page, i18n( "O&ptions" ) );
+}
+
+void DiffPage::addExcludeTab()
+{
+ QWidget* page = new QWidget( this );
+ QVBoxLayout* layout = new QVBoxLayout( page );
+ layout->setSpacing( KDialog::spacingHint() );
+ layout->setMargin( KDialog::marginHint() );
+
+ QHGroupBox* excludeFilePatternGroupBox = new QHGroupBox( i18n( "File Pattern to Exclude" ), page );
+ m_excludeFilePatternCheckBox = new QCheckBox( "", excludeFilePatternGroupBox );
+ QToolTip::add( m_excludeFilePatternCheckBox, 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 KEditListBox( excludeFilePatternGroupBox, "exclude_file_pattern_editlistbox", false, KEditListBox::Add|KEditListBox::Remove );
+ QToolTip::add( m_excludeFilePatternEditListBox, i18n( "Here you can enter or remove a shell pattern or select one or more entries from the list." ) );
+ layout->addWidget( excludeFilePatternGroupBox );
+
+
+ connect( m_excludeFilePatternCheckBox, SIGNAL(toggled(bool)), this, SLOT(slotExcludeFilePatternToggled(bool)));
+
+ QHGroupBox* excludeFileNameGroupBox = new QHGroupBox( i18n( "File with Filenames to Exclude" ), page );
+ m_excludeFileCheckBox = new QCheckBox( "", excludeFileNameGroupBox );
+ QToolTip::add( m_excludeFileCheckBox, i18n( "If this is checked you can enter a filename in the combo box on the right." ) );
+ m_excludeFileURLComboBox = new KURLComboBox( KURLComboBox::Files, true, excludeFileNameGroupBox, "exclude_file_urlcombo" );
+ QToolTip::add( m_excludeFileURLComboBox, 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, excludeFileNameGroupBox, "exclude_file_name_urlrequester" );
+ QToolTip::add( m_excludeFileURLRequester, 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( excludeFileNameGroupBox );
+
+ connect( m_excludeFileCheckBox, SIGNAL(toggled(bool)), this, SLOT(slotExcludeFileToggled(bool)));
+
+ layout->addStretch( 1 );
+ page->setMinimumSize( sizeHintForWidget( page ) );
+
+ addTab( page, i18n( "&Exclude" ) );
+}
+
+#include "diffpage.moc"
diff --git a/kompare/libdialogpages/diffpage.h b/kompare/libdialogpages/diffpage.h
new file mode 100644
index 00000000..dee23989
--- /dev/null
+++ b/kompare/libdialogpages/diffpage.h
@@ -0,0 +1,100 @@
+/***************************************************************************
+ diffprefs.h - description
+ -------------------
+ begin : Sun Mar 4 2001
+ copyright : (C) 2001-2004 Otto Bruggeman
+ (C) 2001-2003 John Firebaugh
+ email : otto.bruggeman@home.nl
+ jfirebaugh@kde.org
+****************************************************************************/
+
+/***************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+***************************************************************************/
+
+#ifndef DIFFPAGE_H
+#define DIFFPAGE_H
+
+#include "pagebase.h"
+
+class QCheckBox;
+class QDialog;
+class QSpinBox;
+class QStringList;
+class QVButtonGroup;
+class QWidget;
+
+class KLineEdit;
+class KComboBox;
+class KEditListBox;
+class KURLComboBox;
+class KURLRequester;
+
+class DiffSettings;
+
+class DiffPage : public PageBase
+{
+Q_OBJECT
+public:
+ DiffPage( QWidget* );
+ ~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_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;
+
+ QCheckBox* m_excludeFilePatternCheckBox;
+ KEditListBox* m_excludeFilePatternEditListBox;
+
+ QCheckBox* m_excludeFileCheckBox;
+ KURLComboBox* m_excludeFileURLComboBox;
+ KURLRequester* m_excludeFileURLRequester;
+
+ // loc == lines of context
+ QSpinBox* m_locSpinBox;
+
+ QVButtonGroup* m_modeButtonGroup;
+ QVButtonGroup* m_diffProgramGroup;
+};
+
+#endif
diff --git a/kompare/libdialogpages/diffsettings.cpp b/kompare/libdialogpages/diffsettings.cpp
new file mode 100644
index 00000000..20504ffe
--- /dev/null
+++ b/kompare/libdialogpages/diffsettings.cpp
@@ -0,0 +1,108 @@
+/***************************************************************************
+ diffsettings.cpp - description
+ -------------------
+ begin : Sun Mar 4 2001
+ copyright : (C) 2001-2004 Otto Bruggeman
+ email : otto.bruggeman@home.nl
+****************************************************************************/
+
+/***************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the 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 <kconfig.h>
+
+#include "diffsettings.h"
+
+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.readNumEntry ( "LinesOfContext", 3 );
+ m_largeFiles = group.readBoolEntry( "LargeFiles", true );
+ m_ignoreWhiteSpace = group.readBoolEntry( "IgnoreWhiteSpace", false );
+ m_ignoreAllWhiteSpace = group.readBoolEntry( "IgnoreAllWhiteSpace", false );
+ m_ignoreEmptyLines = group.readBoolEntry( "IgnoreEmptyLines", false );
+ m_ignoreChangesDueToTabExpansion = group.readBoolEntry( "IgnoreChangesDueToTabExpansion", false );
+ m_ignoreChangesInCase = group.readBoolEntry( "IgnoreChangesInCase", false );
+ m_ignoreRegExp = group.readBoolEntry( "IgnoreRegExp", false );
+ m_ignoreRegExpText = group.readEntry ( "IgnoreRegExpText", "" );
+ m_ignoreRegExpTextHistory = group.readListEntry( "IgnoreRegExpTextHistory" );
+ m_createSmallerDiff = group.readBoolEntry( "CreateSmallerDiff", true );
+ m_convertTabsToSpaces = group.readBoolEntry( "ConvertTabsToSpaces", false );
+ m_showCFunctionChange = group.readBoolEntry( "ShowCFunctionChange", false );
+ m_recursive = group.readBoolEntry( "CompareRecursively", true );
+ m_newFiles = group.readBoolEntry( "NewFiles", true );
+
+ m_format = static_cast<Kompare::Format>( group.readNumEntry( "Format", Kompare::Unified ) );
+
+ KConfigGroup group2 ( config, "Exclude File Options" );
+ m_excludeFilePattern = group2.readBoolEntry( "Pattern", false );
+ m_excludeFilePatternList = group2.readListEntry( "PatternList" );
+ m_excludeFilesFile = group2.readBoolEntry( "File", false );
+ m_excludeFilesFileURL = group2.readEntry ( "FileURL", "" );
+ m_excludeFilesFileHistoryList = group2.readListEntry( "FileHistoryList" );
+}
+
+void DiffSettings::saveSettings( KConfig* config )
+{
+ KConfigGroup group( config, "Diff Options" );
+ group.writeEntry( "DiffProgram", m_diffProgram );
+ group.writeEntry( "LinesOfContext", m_linesOfContext );
+ group.writeEntry( "Format", 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 );
+ group.setDirty( true );
+
+ 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 );
+ group2.setDirty( true );
+}
+
+#include "diffsettings.moc"
diff --git a/kompare/libdialogpages/diffsettings.h b/kompare/libdialogpages/diffsettings.h
new file mode 100644
index 00000000..46962cb8
--- /dev/null
+++ b/kompare/libdialogpages/diffsettings.h
@@ -0,0 +1,66 @@
+/***************************************************************************
+ diffsettings.h - description
+ -------------------
+ begin : Sun Mar 4 2001
+ copyright : (C) 2001-2003 by Otto Bruggeman
+ and John Firebaugh
+ email : otto.bruggeman@home.nl
+ jfirebaugh@kde.org
+****************************************************************************/
+
+/***************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+***************************************************************************/
+
+#ifndef DIFFSETTINGS_H
+#define DIFFSETTINGS_H
+
+#include <qstringlist.h>
+#include <qwidget.h>
+
+#include "kompare.h"
+#include "settingsbase.h"
+
+class 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/kompare/libdialogpages/filespage.cpp b/kompare/libdialogpages/filespage.cpp
new file mode 100644
index 00000000..9e15a129
--- /dev/null
+++ b/kompare/libdialogpages/filespage.cpp
@@ -0,0 +1,170 @@
+/***************************************************************************
+ filespage.cpp
+ -------------------
+ begin : Sun Apr 18 2004
+ copyright : (C) 2004 Otto Bruggeman
+ email : otto.bruggeman@home.nl
+
+****************************************************************************/
+
+/***************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the 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 <qlayout.h>
+#include <qgroupbox.h>
+
+#include <kapplication.h>
+#include <kcharsets.h>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <kdialog.h>
+#include <klocale.h>
+#include <kurlcombobox.h>
+#include <kurlrequester.h>
+
+#include "filessettings.h"
+#include "filespage.h"
+
+FilesPage::FilesPage( QWidget* parent ) : PageBase( parent ), m_URLChanged( false )
+{
+ QWidget* page = new QWidget( this );
+ QVBoxLayout* layout = new QVBoxLayout( page );
+ layout->setSpacing( KDialog::spacingHint() );
+ layout->setMargin( KDialog::marginHint() );
+
+ m_firstGB = new QGroupBox( 1, Qt::Vertical, "You have to set this moron :)", page );
+ m_firstURLComboBox = new KURLComboBox( KURLComboBox::Both, true, m_firstGB, "SourceURLComboBox" );
+ m_firstURLRequester = new KURLRequester( m_firstURLComboBox, m_firstGB );
+ m_firstURLRequester->setFocus();
+
+ m_secondGB = new QGroupBox( 1, Qt::Vertical, "This too moron !", page );
+ m_secondURLComboBox = new KURLComboBox( KURLComboBox::Both, true, m_secondGB, "DestURLComboBox" );
+ m_secondURLRequester = new KURLRequester( m_secondURLComboBox, m_secondGB );
+
+ connect( m_firstURLRequester, SIGNAL( urlSelected( const QString & ) ), SLOT( setSecondURL( const QString & ) ) );
+ connect( m_secondURLRequester, SIGNAL( urlSelected( const QString & ) ), SLOT( setFirstURL( const QString & ) ) );
+
+ m_thirdGB = new QGroupBox( 1, Qt::Vertical, i18n( "Encoding" ), page );
+ m_encodingComboBox = new QComboBox( false, m_thirdGB, "encoding_combobox" );
+ m_encodingComboBox->insertStringList( KGlobal::charsets()->availableEncodingNames() );
+
+ 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()
+{
+// kdDebug() << "first : " << m_firstURLComboBox->currentText() << endl;
+// kdDebug() << "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( mode );
+}
+
+void FilesPage::setSecondURLRequesterMode( unsigned int mode )
+{
+ m_secondURLRequester->setMode( 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->setCurrentText( m_settings->m_encoding );
+}
+
+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( "" );
+ m_firstURLComboBox->setURL( KURL( "" ) );
+ m_secondURLComboBox->setURLs( "" );
+ m_secondURLComboBox->setURL( KURL( "" ) );
+ m_encodingComboBox->setCurrentText( "Default" );
+}
+
+void FilesPage::setFirstURL( const QString &url )
+{
+ QString _url = url;
+ if ( !m_URLChanged )
+ {
+ m_firstURLRequester->setURL( _url.remove( url.section( '/', -1 ) ) );
+ m_URLChanged = true;
+ }
+}
+
+void FilesPage::setSecondURL( const QString &url )
+{
+ QString _url = url;
+ if ( !m_URLChanged )
+ {
+ m_secondURLRequester->setURL( _url.remove( url.section( '/', -1 ) ) );
+ m_URLChanged = true;
+ }
+}
+
+#include "filespage.moc"
diff --git a/kompare/libdialogpages/filespage.h b/kompare/libdialogpages/filespage.h
new file mode 100644
index 00000000..145c4614
--- /dev/null
+++ b/kompare/libdialogpages/filespage.h
@@ -0,0 +1,83 @@
+/***************************************************************************
+ kcompareurldialog.h - description
+ -------------------
+ begin : Sun Mar 4 2001
+ copyright : (C) 2001-2004 Otto Bruggeman
+ (C) 2001-2003 John Firebaugh
+ email : otto.bruggeman@home.nl
+ jfirebaugh@kde.org
+****************************************************************************/
+
+/***************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+***************************************************************************/
+
+#ifndef FILESPAGE_H
+#define FILESPAGE_H
+
+#include "pagebase.h"
+
+class QGroupBox;
+
+class QComboBox;
+class KComboBox;
+class KConfig;
+class KFileDialog;
+class KURLComboBox;
+class KURLRequester;
+
+class FilesSettings;
+
+class FilesPage : PageBase
+{
+Q_OBJECT
+public:
+ FilesPage( QWidget* parent );
+ 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();
+
+protected slots:
+ void setFirstURL( const QString & );
+ void setSecondURL( const QString & );
+
+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;
+ QComboBox* m_encodingComboBox;
+
+ FilesSettings* m_settings;
+};
+
+#endif
diff --git a/kompare/libdialogpages/filessettings.cpp b/kompare/libdialogpages/filessettings.cpp
new file mode 100644
index 00000000..d5a94a66
--- /dev/null
+++ b/kompare/libdialogpages/filessettings.cpp
@@ -0,0 +1,60 @@
+/***************************************************************************
+ filessettings.cpp - description
+ -------------------
+ begin : Sun Apr 18 2004
+ copyright : (C) 2004 Otto Bruggeman
+ email : otto.bruggeman@home.nl
+
+****************************************************************************/
+
+/***************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the 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 <kapplication.h>
+#include <kconfig.h>
+
+#include "filessettings.h"
+
+FilesSettings::FilesSettings( QWidget* parent )
+ : SettingsBase( parent )
+{
+}
+
+FilesSettings::~FilesSettings()
+{
+}
+
+void FilesSettings::loadSettings( KConfig* config )
+{
+ config->setGroup( m_configGroupName );
+
+ m_recentSources = config->readListEntry( "Recent Sources" );
+ m_lastChosenSourceURL = config->readEntry ( "LastChosenSourceListEntry", "" );
+ m_recentDestinations = config->readListEntry( "Recent Destinations" );
+ m_lastChosenDestinationURL = config->readEntry ( "LastChosenDestinationListEntry", "" );
+ m_encoding = config->readEntry ( "Encoding", "default" );
+}
+
+void FilesSettings::saveSettings( KConfig* config )
+{
+ config->setGroup( m_configGroupName );
+ config->writeEntry( "Recent Sources", m_recentSources );
+ config->writeEntry( "Recent Destinations", m_recentDestinations );
+ config->writeEntry( "LastChosenSourceListEntry", m_lastChosenSourceURL );
+ config->writeEntry( "LastChosenDestinationListEntry", m_lastChosenDestinationURL );
+ config->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..3c394dbb
--- /dev/null
+++ b/kompare/libdialogpages/filessettings.h
@@ -0,0 +1,53 @@
+/***************************************************************************
+ filessettings.h - description
+ -------------------
+ begin : Sun Apr 18 2004
+ copyright : (C) 2004 Otto Bruggeman
+ email : otto.bruggeman@home.nl
+
+****************************************************************************/
+
+/***************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the 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 <qstring.h>
+
+#include "settingsbase.h"
+
+class KConfig;
+
+class 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..de062634
--- /dev/null
+++ b/kompare/libdialogpages/pagebase.cpp
@@ -0,0 +1,104 @@
+/***************************************************************************
+ prefsbase.cpp - description
+ -------------------
+ begin : Sun Mar 4 2001
+ copyright : (C) 2001 by Otto Bruggeman
+ and John Firebaugh
+ email : otto.bruggeman@home.nl
+ jfirebaugh@kde.org
+****************************************************************************/
+
+/***************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+***************************************************************************/
+
+#include <qlayout.h>
+#include <qobjectlist.h>
+
+#include "pagebase.h"
+
+PageBase::PageBase( QWidget* parent ) : KTabCtl( parent )
+{
+
+}
+
+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;
+ QObjectList *l = (QObjectList*)(widget->children());
+
+ for( uint 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..7aab7b81
--- /dev/null
+++ b/kompare/libdialogpages/pagebase.h
@@ -0,0 +1,49 @@
+/***************************************************************************
+ prefsbase.h - description
+ -------------------
+ begin : Sun Mar 4 2001
+ copyright : (C) 2001 by Otto Bruggeman
+ and John Firebaugh
+ email : otto.bruggeman@home.nl
+ jfirebaugh@kde.org
+****************************************************************************/
+
+/***************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+***************************************************************************/
+
+#ifndef PAGEBASE_H
+#define PAGEBASE_H
+
+#include <qsize.h>
+#include <qwidget.h>
+
+#include <kconfig.h>
+#include <ktabctl.h>
+
+#include "kompare.h"
+
+class PageBase : public KTabCtl
+{
+Q_OBJECT
+public:
+ PageBase( QWidget* );
+ ~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/settingsbase.cpp b/kompare/libdialogpages/settingsbase.cpp
new file mode 100644
index 00000000..dc7b64d6
--- /dev/null
+++ b/kompare/libdialogpages/settingsbase.cpp
@@ -0,0 +1,42 @@
+/***************************************************************************
+ settingsbase.cpp - description
+ -------------------
+ begin : Sun Mar 4 2001
+ copyright : (C) 2001 by Otto Bruggeman
+ and John Firebaugh
+ email : otto.bruggeman@home.nl
+ jfirebaugh@kde.org
+****************************************************************************/
+
+/***************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+***************************************************************************/
+
+#include <qwidget.h>
+
+#include "settingsbase.h"
+
+SettingsBase::SettingsBase( QWidget* parent ) : QObject( parent )
+{
+
+}
+
+SettingsBase::~SettingsBase()
+{
+
+}
+
+void SettingsBase::loadSettings( KConfig* /* config */ )
+{
+}
+
+void SettingsBase::saveSettings( KConfig* /* config */ )
+{
+}
+
+#include "settingsbase.moc"
diff --git a/kompare/libdialogpages/settingsbase.h b/kompare/libdialogpages/settingsbase.h
new file mode 100644
index 00000000..5ec035ba
--- /dev/null
+++ b/kompare/libdialogpages/settingsbase.h
@@ -0,0 +1,42 @@
+/***************************************************************************
+ settingsbase.h - description
+ -------------------
+ begin : Sun Mar 4 2001
+ copyright : (C) 2001 by Otto Bruggeman
+ and John Firebaugh
+ email : otto.bruggeman@home.nl
+ jfirebaugh@kde.org
+****************************************************************************/
+
+/***************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+***************************************************************************/
+
+#ifndef SETTINGSBASE_H
+#define SETTINGSBASE_H
+
+#include <qobject.h>
+
+#include "kompare.h"
+
+class QWidget;
+class KConfig;
+
+class 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/kompare/libdialogpages/viewpage.cpp b/kompare/libdialogpages/viewpage.cpp
new file mode 100644
index 00000000..c4e61e8d
--- /dev/null
+++ b/kompare/libdialogpages/viewpage.cpp
@@ -0,0 +1,179 @@
+/***************************************************************************
+ viewprefs.cpp - description
+ -------------------
+ begin : Sun Mar 4 2001
+ copyright : (C) 2001-2002 by Otto Bruggeman
+ and John Firebaugh
+ email : otto.bruggeman@home.nl
+ jfirebaugh@kde.org
+****************************************************************************/
+
+/***************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+***************************************************************************/
+
+#include <qcheckbox.h>
+#include <qgroupbox.h>
+#include <qhgroupbox.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qspinbox.h>
+
+#include <kapplication.h>
+#include <kcolorbutton.h>
+#include <kdialog.h>
+#include <kfontcombo.h>
+#include <klocale.h>
+
+#include "viewpage.h"
+#include "viewsettings.h"
+
+ViewPage::ViewPage( QWidget* parent ) : PageBase( parent )
+{
+ QWidget* page;
+ QVBoxLayout* layout;
+ QGroupBox* colorGroupBox;
+ QHGroupBox* snolGroupBox;
+ QHGroupBox* tabGroupBox;
+ QLabel* label;
+
+ page = new QWidget( this );
+ layout = new QVBoxLayout( page );
+ layout->setSpacing( KDialog::spacingHint() );
+ layout->setMargin( KDialog::marginHint() );
+
+ // add a groupbox
+ colorGroupBox = new QGroupBox( 2, Qt::Horizontal, i18n( "Colors" ), page );
+ layout->addWidget( colorGroupBox );
+ colorGroupBox->setMargin( KDialog::marginHint() );
+
+ // add the removeColor
+ label = new QLabel( i18n( "Removed color:" ), colorGroupBox );
+ m_removedColorButton = new KColorButton( colorGroupBox );
+ label->setBuddy( m_removedColorButton );
+
+ // add the changeColor
+ label = new QLabel( i18n( "Changed color:" ), colorGroupBox );
+ m_changedColorButton = new KColorButton( colorGroupBox );
+ label->setBuddy( m_changedColorButton );
+
+ // add the addColor
+ label = new QLabel( i18n( "Added color:" ), colorGroupBox );
+ m_addedColorButton = new KColorButton( colorGroupBox );
+ label->setBuddy( m_addedColorButton );
+
+ // add the appliedColor
+ label = new QLabel( i18n( "Applied color:" ), colorGroupBox );
+ m_appliedColorButton = new KColorButton( colorGroupBox );
+ label->setBuddy( m_appliedColorButton );
+
+ // scroll number of lines (snol)
+ snolGroupBox = new QHGroupBox( i18n( "Mouse Wheel" ), page );
+ layout->addWidget( snolGroupBox );
+ snolGroupBox->setMargin( KDialog::marginHint() );
+
+ label = new QLabel( i18n( "Number of lines:" ), snolGroupBox );
+ m_snolSpinBox = new QSpinBox( 0, 50, 1, snolGroupBox );
+ label->setBuddy( m_snolSpinBox );
+
+ // Temporarily here for testing...
+ // number of spaces for a tab character stuff
+ tabGroupBox = new QHGroupBox( i18n( "Tabs to Spaces" ), page );
+ layout->addWidget( tabGroupBox );
+ tabGroupBox->setMargin( KDialog::marginHint() );
+
+ label = new QLabel( i18n( "Number of spaces to convert a tab character to:" ), tabGroupBox );
+ m_tabSpinBox = new QSpinBox( 1, 16, 1, tabGroupBox );
+ label->setBuddy( m_tabSpinBox );
+
+ layout->addStretch( 1 );
+ page->setMinimumSize( sizeHintForWidget( page ) );
+
+ addTab( page, i18n( "A&ppearance" ) );
+
+ page = new QWidget( this );
+ layout = new QVBoxLayout( page );
+ layout->setSpacing( KDialog::spacingHint() );
+ layout->setMargin( KDialog::marginHint() );
+
+ QHGroupBox* gb = new QHGroupBox( i18n( "Text Font" ), page );
+ layout->addWidget( gb );
+ gb->setMargin( KDialog::marginHint() );
+
+ label = new QLabel( i18n( "Font:" ), gb );
+ m_fontCombo = new KFontCombo( gb, "fontcombo" );
+ label->setBuddy( m_fontCombo );
+
+ label = new QLabel( i18n( "Size:" ), gb );
+ m_fontSizeSpinBox = new QSpinBox( 6, 24, 1, gb, "fontsize" );
+ label->setBuddy( m_fontSizeSpinBox );
+
+ layout->addStretch( 1 );
+ page->setMinimumSize( sizeHintForWidget( page ) );
+
+ 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( kapp->config() );
+}
+
+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 );
+
+ 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..30591f4b
--- /dev/null
+++ b/kompare/libdialogpages/viewpage.h
@@ -0,0 +1,64 @@
+/***************************************************************************
+ generalprefs.h - description
+ -------------------
+ begin : Sun Mar 4 2001
+ copyright : (C) 2001-2003 by Otto Bruggeman
+ and John Firebaugh
+ email : otto.bruggeman@home.nl
+ jfirebaugh@kde.org
+****************************************************************************/
+
+/***************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+***************************************************************************/
+
+#ifndef VIEWPAGE_H
+#define VIEWPAGE_H
+
+#include "pagebase.h"
+
+class QCheckBox;
+class QSpinBox;
+
+class KColorButton;
+class KFontCombo;
+
+class ViewSettings;
+
+class ViewPage : public PageBase
+{
+Q_OBJECT
+public:
+ ViewPage( QWidget* );
+ ~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;
+ KFontCombo* m_fontCombo;
+ QSpinBox* m_fontSizeSpinBox;
+};
+
+#endif
diff --git a/kompare/libdialogpages/viewsettings.cpp b/kompare/libdialogpages/viewsettings.cpp
new file mode 100644
index 00000000..c55dd40e
--- /dev/null
+++ b/kompare/libdialogpages/viewsettings.cpp
@@ -0,0 +1,101 @@
+/***************************************************************************
+ generalsettings.cpp - description
+ -------------------
+ begin : Sun Mar 4 2001
+ copyright : (C) 2001-2003 by Otto Bruggeman
+ and John Firebaugh
+ email : otto.bruggeman@home.nl
+ jfirebaugh@kde.org
+****************************************************************************/
+
+/***************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+***************************************************************************/
+
+#include <qfont.h>
+
+#include <kconfig.h>
+#include <kglobalsettings.h>
+
+#include "viewsettings.h"
+
+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.readColorEntry( "RemoveColor", &default_removeColor );
+ m_changeColor = cfg.readColorEntry( "ChangeColor", &default_changeColor );
+ m_addColor = cfg.readColorEntry( "AddColor", &default_addColor );
+ m_appliedColor = cfg.readColorEntry( "AppliedColor", &default_appliedColor );
+ m_scrollNoOfLines = cfg.readNumEntry ( "ScrollNoOfLines", 3 );
+ m_tabToNumberOfSpaces = cfg.readNumEntry ( "TabToNumberOfSpaces", 4 );
+
+ QFont stdFixed = KGlobalSettings::fixedFont();
+ stdFixed.setPointSize( 10 );
+ m_font = cfg.readFontEntry ( "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 = 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..ab0f4d5f
--- /dev/null
+++ b/kompare/libdialogpages/viewsettings.h
@@ -0,0 +1,61 @@
+/***************************************************************************
+ generalsettings.h - description
+ -------------------
+ begin : Sun Mar 4 2001
+ copyright : (C) 2001-2003 by Otto Bruggeman
+ and John Firebaugh
+ email : otto.bruggeman@home.nl
+ jfirebaugh@kde.org
+****************************************************************************/
+
+/***************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+***************************************************************************/
+
+#ifndef VIEWSETTINGS_H
+#define VIEWSETTINGS_H
+
+#include <qcolor.h>
+#include <qwidget.h>
+
+#include "difference.h"
+#include "settingsbase.h"
+
+class 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/libdiff2/Makefile.am b/kompare/libdiff2/Makefile.am
new file mode 100644
index 00000000..6f9048d8
--- /dev/null
+++ b/kompare/libdiff2/Makefile.am
@@ -0,0 +1,37 @@
+INCLUDES = \
+ -I$(top_srcdir)/kompare/libdialogpages \
+ -I$(top_srcdir)/kompare/komparepart \
+ -I$(top_srcdir)/kompare/interfaces $(all_includes)
+
+noinst_HEADERS = \
+ levenshteintable.h \
+ kompare.h \
+ kompareprocess.h \
+ komparemodellist.h \
+ diffmodel.h \
+ difference.h \
+ diffhunk.h
+
+# let automoc handle all of the meta source files (moc)
+METASOURCES = AUTO
+
+noinst_LTLIBRARIES = libdiff2.la
+
+# the Part's source, library search path, and link libraries
+libdiff2_la_SOURCES = \
+ kompareprocess.cpp \
+ komparemodellist.cpp \
+ diffmodellist.cpp \
+ diffmodel.cpp \
+ difference.cpp \
+ diffhunk.cpp \
+ levenshteintable.cpp \
+ parser.cpp \
+ parserbase.cpp \
+ cvsdiffparser.cpp \
+ diffparser.cpp \
+ perforceparser.cpp
+
+libdiff2_la_LDFLAGS = $(all_libraries)
+libdiff2_la_LIBADD = $(LIB_KFILE)
+
diff --git a/kompare/libdiff2/cvsdiffparser.cpp b/kompare/libdiff2/cvsdiffparser.cpp
new file mode 100644
index 00000000..d210eb66
--- /dev/null
+++ b/kompare/libdiff2/cvsdiffparser.cpp
@@ -0,0 +1,160 @@
+/**************************************************************************
+** cvsdiffparser.cpp
+** -----------------
+** begin : Sun Aug 4 15:05:35 2002
+** copyright : (C) 2002-2004 Otto Bruggeman
+** email : otto.bruggeman@home.nl
+**
+***************************************************************************/
+/***************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the 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 <qregexp.h>
+
+#include <kdebug.h>
+
+#include "cvsdiffparser.h"
+#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()
+{
+// kdDebug(8101) << "Determining the format of the CVSDiff" << endl;
+
+ 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).find( normalRE, 0 ) == 0 )
+ {
+// kdDebug(8101) << "Difflines are from a Normal diff..." << endl;
+ return Kompare::Normal;
+ }
+ else if( (*it).find( unifiedRE, 0 ) == 0 )
+ {
+// kdDebug(8101) << "Difflines are from a Unified diff..." << endl;
+ return Kompare::Unified;
+ }
+ else if( (*it).find( contextRE, 0 ) == 0 )
+ {
+// kdDebug(8101) << "Difflines are from a Context diff..." << endl;
+ return Kompare::Context;
+ }
+ else if( (*it).find( rcsRE, 0 ) == 0 )
+ {
+// kdDebug(8101) << "Difflines are from a RCS diff..." << endl;
+ return Kompare::RCS;
+ }
+ else if( (*it).find( edRE, 0 ) == 0 )
+ {
+// kdDebug(8101) << "Difflines are from an ED diff..." << endl;
+ return Kompare::Ed;
+ }
+ ++it;
+ }
+// kdDebug(8101) << "Difflines are from an unknown diff..." << endl;
+ return Kompare::UnknownFormat;
+}
+
+bool CVSDiffParser::parseNormalDiffHeader()
+{
+ kdDebug(8101) << "CVSDiffParser::parseNormalDiffHeader()" << endl;
+ bool result = false;
+
+ QStringList::ConstIterator diffEnd = m_diffLines.end();
+
+ while ( m_diffIterator != diffEnd )
+ {
+ if ( m_normalDiffHeader.exactMatch( *m_diffIterator ) )
+ {
+ kdDebug(8101) << "Matched length Header = " << m_normalDiffHeader.matchedLength() << endl;
+ kdDebug(8101) << "Matched string Header = " << m_normalDiffHeader.cap( 0 ) << endl;
+
+ m_currentModel = new DiffModel();
+ QObject::connect( m_currentModel, SIGNAL( setModified( bool ) ), m_list, SLOT( slotSetModified( bool ) ) );
+ m_currentModel->setSourceFile ( m_normalDiffHeader.cap( 1 ) );
+ m_currentModel->setDestinationFile ( m_normalDiffHeader.cap( 1 ) );
+
+ result = true;
+
+ ++m_diffIterator;
+ break;
+ }
+ else
+ {
+ kdDebug(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();
+ QObject::connect( m_currentModel, SIGNAL( setModified( bool ) ), m_list, SLOT( slotSetModified( bool ) ) );
+ 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/kompare/libdiff2/cvsdiffparser.h b/kompare/libdiff2/cvsdiffparser.h
new file mode 100644
index 00000000..88fef485
--- /dev/null
+++ b/kompare/libdiff2/cvsdiffparser.h
@@ -0,0 +1,61 @@
+/**************************************************************************
+** cvsdiffparser.h
+** -----------------
+** begin : Sun Aug 4 15:05:35 2002
+** copyright : (C) 2002-2004 Otto Bruggeman
+** email : otto.bruggeman@home.nl
+**
+***************************************************************************/
+/***************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the 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 <qregexp.h>
+
+#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/kompare/libdiff2/difference.cpp b/kompare/libdiff2/difference.cpp
new file mode 100644
index 00000000..8cbb4093
--- /dev/null
+++ b/kompare/libdiff2/difference.cpp
@@ -0,0 +1,137 @@
+/***************************************************************************
+ difference.cpp - description
+ -------------------
+ begin : Sun Mar 4 2001
+ copyright : (C) 2001-2003 by Otto Bruggeman
+ and John Firebaugh
+ email : otto.bruggeman@home.nl
+ jfirebaugh@kde.org
+****************************************************************************/
+
+/***************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+***************************************************************************/
+
+#include "difference.h"
+#include "levenshteintable.h"
+
+using namespace Diff2;
+
+Difference::Difference( int sourceLineNo, int destinationLineNo, int type ) :
+ m_type( type ),
+ m_sourceLineNo( sourceLineNo ),
+ m_destinationLineNo( destinationLineNo ),
+ m_applied( false )
+{
+}
+
+Difference::~Difference()
+{
+}
+
+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();
+}
+
+void Difference::apply( bool apply )
+{
+ m_applied = apply;
+}
+
+void Difference::determineInlineDifferences()
+{
+ LevenshteinTable table;
+ 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
+ if ( sourceLineCount() != destinationLineCount() )
+ return;
+
+ int slc = sourceLineCount();
+
+ for ( int i = 0; i < slc; ++i )
+ {
+ DifferenceString* sl = sourceLineAt( i );
+ DifferenceString* dl = destinationLineAt( i );
+
+ // FIXME: If the table cant be created dont do the rest
+ table.createTable( sl, dl );
+
+ 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
+// kdDebug( 8101 ) << "Go away, nothing to do for you in source..." << endl;
+ 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
+// kdDebug( 8101 ) << "Go away, nothing to do for you in destination..." << endl;
+ continue;
+ }
+ difference += (*stringIt)->string();
+ }
+
+ return difference;
+}
diff --git a/kompare/libdiff2/difference.h b/kompare/libdiff2/difference.h
new file mode 100644
index 00000000..91065891
--- /dev/null
+++ b/kompare/libdiff2/difference.h
@@ -0,0 +1,223 @@
+/***************************************************************************
+ difference.h - description
+ -------------------
+ begin : Sun Mar 4 2001
+ copyright : (C) 2001-2003 by Otto Bruggeman
+ and John Firebaugh
+ email : otto.bruggeman@home.nl
+ jfirebaugh@kde.org
+****************************************************************************/
+
+/***************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+***************************************************************************/
+
+#ifndef DIFFERENCE_H
+#define DIFFERENCE_H
+
+#include <qvaluelist.h>
+#include <qvaluevector.h>
+
+#include <kdebug.h>
+
+class QString;
+
+namespace Diff2
+{
+
+class LevenshteinTable;
+
+class 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; }
+
+private:
+ enum Marker::Type m_type;
+ unsigned int m_offset;
+};
+
+typedef QValueList<Marker*> MarkerList;
+typedef QValueList<Marker*>::iterator MarkerListIterator;
+typedef QValueList<Marker*>::const_iterator MarkerListConstIterator;
+
+class DifferenceString
+{
+public:
+ DifferenceString()
+ {
+// kdDebug(8101) << "DifferenceString::DifferenceString()" << endl;
+ }
+ DifferenceString( const QString& string, const MarkerList& markerList = MarkerList() ) :
+ m_string( string ),
+ m_markerList( markerList )
+ {
+// kdDebug(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 )
+ {
+// kdDebug(8101) << "DifferenceString::DifferenceString( const DifferenceString& " << ds << " )" << endl;
+ }
+ ~DifferenceString() {}
+
+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<unsigned short const*>( 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 QValueVector<DifferenceString*> DifferenceStringList;
+typedef QValueVector<DifferenceString*>::iterator DifferenceStringListIterator;
+typedef QValueVector<DifferenceString*>::const_iterator DifferenceStringListConstIterator;
+
+class Difference
+{
+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;
+
+ DifferenceString* sourceLineAt( int i ) { return m_sourceLines[ i ]; }
+ DifferenceString* destinationLineAt( int i ) { 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;
+ }
+
+ void apply( 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;
+
+private:
+ int m_type;
+
+ int m_sourceLineNo;
+ int m_destinationLineNo;
+
+ DifferenceStringList m_sourceLines;
+ DifferenceStringList m_destinationLines;
+
+ bool m_applied;
+ bool m_conflicts;
+
+ LevenshteinTable* m_tableXXX; // now unused
+};
+
+typedef QValueList<Difference*> DifferenceList;
+typedef QValueList<Difference*>::iterator DifferenceListIterator;
+typedef QValueList<Difference*>::const_iterator DifferenceListConstIterator;
+
+} // End of namespace Diff2
+
+#endif
+
diff --git a/kompare/libdiff2/diffhunk.cpp b/kompare/libdiff2/diffhunk.cpp
new file mode 100644
index 00000000..f980dd93
--- /dev/null
+++ b/kompare/libdiff2/diffhunk.cpp
@@ -0,0 +1,115 @@
+/***************************************************************************
+ diffhunk.cpp - description
+ -------------------
+ begin : Sun Mar 4 2001
+ copyright : (C) 2001-2003 by Otto Bruggeman
+ and John Firebaugh
+ email : otto.bruggeman@home.nl
+ jfirebaugh@kde.org
+****************************************************************************/
+
+/***************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+***************************************************************************/
+
+
+#include "difference.h"
+#include "diffhunk.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;
+
+ kdDebug( 8101 ) << hunk << endl;
+ return hunk;
+}
diff --git a/kompare/libdiff2/diffhunk.h b/kompare/libdiff2/diffhunk.h
new file mode 100644
index 00000000..8a76babb
--- /dev/null
+++ b/kompare/libdiff2/diffhunk.h
@@ -0,0 +1,69 @@
+/***************************************************************************
+ diffhunk.h - description
+ -------------------
+ begin : Sun Mar 4 2001
+ copyright : (C) 2001-2003 by Otto Bruggeman
+ and John Firebaugh
+ email : otto.bruggeman@home.nl
+ jfirebaugh@kde.org
+****************************************************************************/
+
+/***************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+***************************************************************************/
+
+#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::null, 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;
+
+ 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 QValueList<DiffHunk*> DiffHunkList;
+typedef QValueList<DiffHunk*>::iterator DiffHunkListIterator;
+typedef QValueList<DiffHunk*>::const_iterator DiffHunkListConstIterator;
+
+} // End of namespace Diff2
+
+#endif
diff --git a/kompare/libdiff2/diffmodel.cpp b/kompare/libdiff2/diffmodel.cpp
new file mode 100644
index 00000000..54c33457
--- /dev/null
+++ b/kompare/libdiff2/diffmodel.cpp
@@ -0,0 +1,409 @@
+/***************************************************************************
+ diffmodel.cpp - description
+ -------------------
+ begin : Sun Mar 4 2001
+ copyright : (C) 2001-2004 Otto Bruggeman
+ (C) 2001-2003 John Firebaugh
+ email : otto.bruggeman@home.nl
+ jfirebaugh@kde.org
+****************************************************************************/
+
+/***************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+***************************************************************************/
+
+#include <qregexp.h>
+#include <qvaluelist.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+
+#include "difference.h"
+#include "diffhunk.h"
+#include "diffmodel.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_modified( false ),
+ 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_modified( false ),
+ m_diffIndex( 0 ),
+ m_selectedDifference( 0 ),
+ m_blended( false )
+{
+}
+
+/** */
+DiffModel::~DiffModel()
+{
+}
+
+void DiffModel::splitSourceInPathAndFileName()
+{
+ int pos;
+
+ if( ( pos = m_source.findRev( "/" ) ) >= 0 )
+ m_sourcePath = m_source.mid( 0, pos+1 );
+
+ if( ( pos = m_source.findRev( "/" ) ) >= 0 )
+ m_sourceFile = m_source.mid( pos+1, m_source.length() - pos );
+ else
+ m_sourceFile = m_source;
+
+ kdDebug(8101) << m_source << " was split into " << m_sourcePath << " and " << m_sourceFile << endl;
+}
+
+void DiffModel::splitDestinationInPathAndFileName()
+{
+ int pos;
+
+ if( ( pos = m_destination.findRev( "/" ) )>= 0 )
+ m_destinationPath = m_destination.mid( 0, pos+1 );
+
+ if( ( pos = m_destination.findRev( "/" ) ) >= 0 )
+ m_destinationFile = m_destination.mid( pos+1, m_destination.length() - pos );
+ else
+ m_destinationFile = m_source;
+
+ kdDebug(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_modified = model.m_modified;
+
+ 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 )
+{
+ 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;
+}
+
+DifferenceList* DiffModel::allDifferences()
+{
+ if ( m_hunks.count() != 0 )
+ {
+ DiffHunkListConstIterator hunkIt = m_hunks.begin();
+ DiffHunkListConstIterator hEnd = m_hunks.end();
+
+ for ( ; hunkIt != hEnd; ++hunkIt )
+ {
+ DiffHunk* hunk = *hunkIt;
+
+ DifferenceListConstIterator diffIt = hunk->differences().begin();
+ DifferenceListConstIterator dEnd = hunk->differences().end();
+
+ for ( ; diffIt != dEnd; ++diffIt )
+ {
+ m_allDifferences.append( *diffIt );
+ }
+ }
+ return &m_allDifferences;
+ }
+ else
+ {
+ DifferenceList *diffList = new DifferenceList;
+ return diffList;
+ }
+}
+
+Difference* DiffModel::firstDifference()
+{
+ kdDebug( 8101 ) << "DiffModel::firstDifference()" << endl;
+ m_diffIndex = 0;
+ kdDebug( 8101 ) << "m_diffIndex = " << m_diffIndex << endl;
+
+ m_selectedDifference = m_differences[ m_diffIndex ];
+
+ return m_selectedDifference;
+}
+
+Difference* DiffModel::lastDifference()
+{
+ kdDebug( 8101 ) << "DiffModel::lastDifference()" << endl;
+ m_diffIndex = m_differences.count() - 1;
+ kdDebug( 8101 ) << "m_diffIndex = " << m_diffIndex << endl;
+
+ m_selectedDifference = m_differences[ m_diffIndex ];
+
+ return m_selectedDifference;
+}
+
+Difference* DiffModel::prevDifference()
+{
+ kdDebug( 8101 ) << "DiffModel::prevDifference()" << endl;
+ if ( --m_diffIndex < m_differences.count() )
+ {
+ kdDebug( 8101 ) << "m_diffIndex = " << m_diffIndex << endl;
+ m_selectedDifference = m_differences[ m_diffIndex ];
+ }
+ else
+ {
+ m_selectedDifference = 0;
+ m_diffIndex = 0;
+ kdDebug( 8101 ) << "m_diffIndex = " << m_diffIndex << endl;
+ }
+
+ return m_selectedDifference;
+}
+
+Difference* DiffModel::nextDifference()
+{
+ kdDebug( 8101 ) << "DiffModel::nextDifference()" << endl;
+ if ( ++m_diffIndex < m_differences.count() )
+ {
+ kdDebug( 8101 ) << "m_diffIndex = " << m_diffIndex << endl;
+ m_selectedDifference = m_differences[ m_diffIndex ];
+ }
+ else
+ {
+ m_selectedDifference = 0;
+ m_diffIndex = 0; // just for safety...
+ kdDebug( 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_destinationRevision = 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 );
+}
+
+void DiffModel::applyDifference( bool apply )
+{
+ if ( apply && !m_selectedDifference->applied() )
+ m_appliedCount++;
+ else if ( !apply && m_selectedDifference->applied() )
+ m_appliedCount--;
+
+ bool modified;
+
+ // Not setting the m_modified yet so i can still query the current
+ // modified status from the slot that is connected to the signal
+ if ( m_appliedCount == 0 )
+ modified = false;
+ else
+ modified = true;
+
+ emit setModified( modified );
+
+ m_modified = modified;
+
+ m_selectedDifference->apply( apply );
+}
+
+void DiffModel::applyAllDifferences( bool apply )
+{
+ bool modified;
+
+ // Not setting the m_modified yet so i can still query the current
+ // modified status from the slot that is connected to the signal
+ if ( apply )
+ {
+ m_appliedCount = m_differences.count();
+ modified = true;
+ }
+ else
+ {
+ m_appliedCount = 0;
+ modified = false;
+ }
+
+ emit setModified( modified );
+
+ m_modified = modified;
+
+ DifferenceListIterator diffIt = m_differences.begin();
+ DifferenceListIterator dEnd = m_differences.end();
+
+ for ( ; diffIt != dEnd; ++diffIt )
+ {
+ (*diffIt)->apply( apply );
+ }
+}
+
+void DiffModel::slotSetModified( bool modified )
+{
+ // Not setting the m_modified yet so i can still query the current
+ // modified status from the slot that is connected to the signal
+ emit setModified( modified );
+
+ m_modified = modified;
+}
+
+bool DiffModel::setSelectedDifference( Difference* diff )
+{
+ kdDebug(8101) << "diff = " << diff << endl;
+ kdDebug(8101) << "m_selectedDifference = " << m_selectedDifference << endl;
+
+ if ( diff != m_selectedDifference )
+ {
+ if ( ( m_differences.findIndex( diff ) ) == -1 )
+ return false;
+ // Dont set m_diffIndex if it cant be found
+ m_diffIndex = m_differences.findIndex( diff );
+ kdDebug( 8101 ) << "m_diffIndex = " << m_diffIndex << endl;
+ m_selectedDifference = diff;
+ }
+
+ return true;
+}
+
+#include "diffmodel.moc"
+
+/* vim: set ts=4 sw=4 noet: */
diff --git a/kompare/libdiff2/diffmodel.h b/kompare/libdiff2/diffmodel.h
new file mode 100644
index 00000000..11c424b5
--- /dev/null
+++ b/kompare/libdiff2/diffmodel.h
@@ -0,0 +1,150 @@
+/***************************************************************************
+ diffmodel.h - description
+ -------------------
+ begin : Sun Mar 4 2001
+ copyright : (C) 2001-2004 Otto Bruggeman
+ (C) 2001-2003 John Firebaugh
+ email : otto.bruggeman@home.nl
+ jfirebaugh@kde.org
+****************************************************************************/
+
+/***************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+***************************************************************************/
+
+#ifndef DIFFMODEL_H
+#define DIFFMODEL_H
+
+#include <qobject.h>
+#include <qstringlist.h>
+
+#include "diffhunk.h"
+#include "kompare.h"
+
+namespace Diff2
+{
+
+class DiffHunk;
+class Difference;
+
+class DiffModel : public QObject
+{
+Q_OBJECT
+public:
+
+ DiffModel( const QString& srcBaseURL, const QString& destBaseURL );
+ DiffModel();
+ DiffModel( const DiffModel& ) : QObject() {};
+ ~DiffModel();
+
+ 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 ) { 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; }
+
+ DifferenceList* allDifferences();
+
+ int findDifference( Difference* diff ) const { return m_differences.findIndex( 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 isModified() const { return m_modified; }
+
+ 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; }
+
+signals:
+ void setModified( bool modified );
+
+public slots:
+ void slotSetModified( bool modified );
+
+private:
+ void splitSourceInPathAndFileName();
+ void splitDestinationInPathAndFileName();
+
+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;
+ DifferenceList m_allDifferences;
+
+ int m_appliedCount;
+ bool m_modified;
+
+ unsigned int m_diffIndex;
+ Difference* m_selectedDifference;
+
+ bool m_blended;
+};
+
+} // End of namespace Diff2
+
+#endif
+
diff --git a/kompare/libdiff2/diffmodellist.cpp b/kompare/libdiff2/diffmodellist.cpp
new file mode 100644
index 00000000..200e8108
--- /dev/null
+++ b/kompare/libdiff2/diffmodellist.cpp
@@ -0,0 +1,29 @@
+/*******************************************************************************
+**
+** Filename : diffmodellist.cpp
+** Created on : 26 march, 2004
+** Copyright : (c) 2004 Otto Bruggeman
+** Email : bruggie@home.nl
+**
+*******************************************************************************/
+
+/*******************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the 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 <kdebug.h>
+
+#include "diffmodellist.h"
+
+using namespace Diff2;
+
+void DiffModelList::sort()
+{
+ qHeapSort(*this);
+}
+
diff --git a/kompare/libdiff2/diffmodellist.h b/kompare/libdiff2/diffmodellist.h
new file mode 100644
index 00000000..9c4f9807
--- /dev/null
+++ b/kompare/libdiff2/diffmodellist.h
@@ -0,0 +1,49 @@
+/*******************************************************************************
+**
+** Filename : diffmodellist.h
+** Created on : 24 januari, 2004
+** Copyright : (c) 2004 Otto Bruggeman
+** Email : bruggie@home.nl
+**
+*******************************************************************************/
+
+/*******************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the 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 <qvaluelist.h> // include for the base class
+
+#include "diffmodel.h"
+
+namespace Diff2
+{
+
+typedef QValueListIterator<DiffModel*> DiffModelListIterator;
+typedef QValueListConstIterator<DiffModel*> DiffModelListConstIterator;
+
+class DiffModelList : public QValueList<DiffModel*>
+{
+public:
+ DiffModelList() {}
+ DiffModelList( const DiffModelList &list ) : QValueList<DiffModel*>( list ) {}
+ virtual ~DiffModelList()
+ {
+ clear();
+ }
+
+public:
+ virtual void sort();
+
+}; // End of class DiffModelList
+
+} // End of Namespace Diff2
+
+#endif // DIFFMODELLIST_H
diff --git a/kompare/libdiff2/diffparser.cpp b/kompare/libdiff2/diffparser.cpp
new file mode 100644
index 00000000..f98fbde5
--- /dev/null
+++ b/kompare/libdiff2/diffparser.cpp
@@ -0,0 +1,81 @@
+/**************************************************************************
+** diffparser.cpp
+** --------------
+** begin : Sun Aug 4 15:05:35 2002
+** copyright : (C) 2002-2004 Otto Bruggeman
+** email : otto.bruggeman@home.nl
+**
+***************************************************************************/
+/***************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the 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 <qregexp.h>
+
+#include <kdebug.h>
+
+#include "diffparser.h"
+
+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()
+{
+ kdDebug(8101) << "Determining the format of the diff Diff" << 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() )
+ {
+ kdDebug(8101) << (*it) << endl;
+ if( (*it).find( normalRE, 0 ) == 0 )
+ {
+ kdDebug(8101) << "Difflines are from a Normal diff..." << endl;
+ return Kompare::Normal;
+ }
+ else if( (*it).find( unifiedRE, 0 ) == 0 )
+ {
+ kdDebug(8101) << "Difflines are from a Unified diff..." << endl;
+ return Kompare::Unified;
+ }
+ else if( (*it).find( contextRE, 0 ) == 0 )
+ {
+ kdDebug(8101) << "Difflines are from a Context diff..." << endl;
+ return Kompare::Context;
+ }
+ else if( (*it).find( rcsRE, 0 ) == 0 )
+ {
+ kdDebug(8101) << "Difflines are from an RCS diff..." << endl;
+ return Kompare::RCS;
+ }
+ else if( (*it).find( edRE, 0 ) == 0 )
+ {
+ kdDebug(8101) << "Difflines are from an ED diff..." << endl;
+ return Kompare::Ed;
+ }
+ ++it;
+ }
+ kdDebug(8101) << "Difflines are from an unknown diff..." << endl;
+ return Kompare::UnknownFormat;
+}
diff --git a/kompare/libdiff2/diffparser.h b/kompare/libdiff2/diffparser.h
new file mode 100644
index 00000000..72905e3f
--- /dev/null
+++ b/kompare/libdiff2/diffparser.h
@@ -0,0 +1,38 @@
+/**************************************************************************
+** diffparser.h
+** -----------------
+** begin : Sun Aug 4 15:05:35 2002
+** copyright : (C) 2002-2004 Otto Bruggeman
+** email : otto.bruggeman@home.nl
+**
+***************************************************************************/
+/***************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the 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 _DIFF_PARSER_H
+#define _DIFF_PARSER_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/kompare/libdiff2/kompare.h b/kompare/libdiff2/kompare.h
new file mode 100644
index 00000000..1ed5c4c7
--- /dev/null
+++ b/kompare/libdiff2/kompare.h
@@ -0,0 +1,144 @@
+/***************************************************************************
+ kompare.h - description
+ -------------------
+ begin : Sun Mar 4 2001
+ copyright : (C) 2001-2003 by Otto Bruggeman
+ and John Firebaugh
+ email : otto.bruggeman@home.nl
+ jfirebaugh@kde.org
+****************************************************************************/
+
+/***************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+***************************************************************************/
+
+#ifndef KOMPARE_H
+#define KOMPARE_H
+
+#include <kurl.h>
+
+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
+ ComparingDirs, // compareDirs
+ ShowingDiff, // openDiff
+ BlendingDir, // openDirAnfDiff
+ 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 = ""
+ )
+ {
+ mode = _mode;
+ diffMode = _diffMode;
+ format = _format;
+ generator = _generator;
+ source = _source;
+ destination = _destination;
+ localSource = _localSource;
+ localDestination = _localDestination;
+ }
+ enum Mode mode;
+ enum DiffMode diffMode;
+ enum Format format;
+ enum Generator generator;
+ KURL source;
+ KURL destination;
+ QString localSource;
+ QString localDestination;
+ };
+} // End of namespace Kompare
+
+/*
+** This should be removed and put somewhere else
+*/
+class 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(1).length(), "" );
+
+ return relative;
+ }
+};
+
+#endif
diff --git a/kompare/libdiff2/komparemodellist.cpp b/kompare/libdiff2/komparemodellist.cpp
new file mode 100644
index 00000000..ac3c725a
--- /dev/null
+++ b/kompare/libdiff2/komparemodellist.cpp
@@ -0,0 +1,1423 @@
+/***************************************************************************
+ komparemodellist.cpp - description
+ -------------------
+ begin : Tue Jun 26 2001
+ copyright : (C) 2001-2004 Otto Bruggeman
+ (C) 2001-2003 John Firebaugh
+ (C) 2007 Kevin Kofler
+ email : jfirebaugh@kde.org
+ otto.bruggeman@home.nl
+ kevin.kofler@chello.at
+***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ ***************************************************************************/
+
+#include <qfile.h>
+#include <qdir.h>
+#include <qregexp.h>
+#include <qtextcodec.h>
+#include <qvaluelist.h>
+
+#include <kaction.h>
+#include <kcharsets.h>
+#include <kdebug.h>
+#include <kdirwatch.h>
+#include <kio/netaccess.h>
+#include <klocale.h>
+#include <kmimetype.h>
+#include <ktempfile.h>
+
+#include "difference.h"
+#include "diffhunk.h"
+#include "diffmodel.h"
+#include "diffmodellist.h"
+#include "kompareprocess.h"
+#include "komparemodellist.h"
+#include "parser.h"
+
+#include "kompare_part.h"
+
+using namespace Diff2;
+
+KompareModelList::KompareModelList( DiffSettings* diffSettings, struct Kompare::Info& info, QObject* parent, const char* name )
+ : QObject( parent, name ),
+ m_diffProcess( 0 ),
+ m_diffSettings( diffSettings ),
+ m_models( 0 ),
+ m_selectedModel( 0 ),
+ m_selectedDifference( 0 ),
+ m_noOfModified( 0 ),
+ m_modelIndex( 0 ),
+ m_info( info ),
+ m_textCodec( 0 )
+{
+ m_applyDifference = new KAction( i18n("&Apply Difference"), "1rightarrow", Qt::Key_Space,
+ this, SLOT(slotActionApplyDifference()),
+ (( KomparePart* )parent)->actionCollection(), "difference_apply" );
+ m_unApplyDifference = new KAction( i18n("Un&apply Difference"), "1leftarrow", Qt::Key_BackSpace,
+ this, SLOT(slotActionUnApplyDifference()),
+ (( KomparePart* )parent)->actionCollection(), "difference_unapply" );
+ m_applyAll = new KAction( i18n("App&ly All"), "2rightarrow", Qt::CTRL + Qt::Key_A,
+ this, SLOT(slotActionApplyAllDifferences()),
+ (( KomparePart* )parent)->actionCollection(), "difference_applyall" );
+ m_unapplyAll = new KAction( i18n("&Unapply All"), "2leftarrow", Qt::CTRL + Qt::Key_U,
+ this, SLOT(slotActionUnapplyAllDifferences()),
+ (( KomparePart* )parent)->actionCollection(), "difference_unapplyall" );
+ m_previousFile = new KAction( i18n("P&revious File"), "2uparrow", Qt::CTRL + Qt::Key_PageUp,
+ this, SLOT(slotPreviousModel()),
+ (( KomparePart* )parent)->actionCollection(), "difference_previousfile" );
+ m_nextFile = new KAction( i18n("N&ext File"), "2downarrow", Qt::CTRL + Qt::Key_PageDown,
+ this, SLOT(slotNextModel()),
+ (( KomparePart* )parent)->actionCollection(), "difference_nextfile" );
+ m_previousDifference = new KAction( i18n("&Previous Difference"), "1uparrow", Qt::CTRL + Qt::Key_Up,
+ this, SLOT(slotPreviousDifference()),
+ (( KomparePart* )parent)->actionCollection(), "difference_previous" );
+ m_nextDifference = new KAction( i18n("&Next Difference"), "1downarrow", Qt::CTRL + Qt::Key_Down,
+ this, SLOT(slotNextDifference()),
+ (( KomparePart* )parent)->actionCollection(), "difference_next" );
+ m_previousDifference->setEnabled( false );
+ m_nextDifference->setEnabled( false );
+
+ m_save = KStdAction::save( this, SLOT(slotSaveDestination()), ((KomparePart*)parent)->actionCollection() );
+ m_save->setEnabled( false );
+
+ updateModelListActions();
+}
+
+KompareModelList::~KompareModelList()
+{
+}
+
+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-diff" )
+ return true;
+ else
+ return false;
+}
+
+bool KompareModelList::compare( const QString& source, const QString& destination )
+{
+ bool result = false;
+
+ bool sourceIsDirectory = isDirectory( source );
+ bool destinationIsDirectory = isDirectory( source );
+
+ if ( sourceIsDirectory && destinationIsDirectory )
+ {
+ m_info.mode = Kompare::ComparingDirs;
+ result = compareDirs( source, destination );
+ }
+ else if ( !sourceIsDirectory && !destinationIsDirectory )
+ {
+ QFile sourceFile( source );
+ sourceFile.open( IO_ReadOnly );
+ QString sourceMimeType = ( KMimeType::findByContent( sourceFile.readAll() ) )->name();
+ sourceFile.close();
+ kdDebug(8101) << "Mimetype source : " << sourceMimeType << endl;
+
+ QFile destinationFile( destination );
+ destinationFile.open( IO_ReadOnly );
+ QString destinationMimeType = ( KMimeType::findByContent( destinationFile.readAll() ) )->name();
+ destinationFile.close();
+ kdDebug(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 ) )
+ {
+ kdDebug(8101) << "Blending destination into source..." << endl;
+ m_info.mode = Kompare::BlendingFile;
+ result = openFileAndDiff( source, destination );
+ }
+ else if ( isDiff( sourceMimeType ) && !isDiff( destinationMimeType ) )
+ {
+ kdDebug(8101) << "Blending source into destination..." << endl;
+ m_info.mode = Kompare::BlendingFile;
+ result = openFileAndDiff( destination, source );
+ }
+ else
+ {
+ kdDebug(8101) << "Comparing source with destination" << endl;
+ m_info.mode = Kompare::ComparingFiles;
+ result = compareFiles( source, destination );
+ }
+ }
+ else if ( sourceIsDirectory && !destinationIsDirectory )
+ {
+ m_info.mode = Kompare::BlendingDir;
+ result = openDirAndDiff( source, destination );
+ }
+ else
+ {
+ m_info.mode = Kompare::BlendingDir;
+ result = openDirAndDiff( destination, source );
+ }
+
+ return result;
+}
+
+bool KompareModelList::compareFiles( const QString& source, const QString& destination )
+{
+ m_source = source;
+ m_destination = destination;
+
+ clear(); // Destroy the old models...
+
+// m_fileWatch = new KDirWatch( this, "filewatch" );
+// m_fileWatch->addFile( m_source );
+// m_fileWatch->addFile( m_destination );
+
+// connect( m_fileWatch, SIGNAL( dirty( const QString& ) ), this, SLOT( slotFileChanged( const QString& ) ) );
+// connect( m_fileWatch, SIGNAL( created( const QString& ) ), this, SLOT( slotFileChanged( const QString& ) ) );
+// connect( m_fileWatch, SIGNAL( deleted( const QString& ) ), this, SLOT( slotFileChanged( const QString& ) ) );
+
+// m_fileWatch->startScan();
+ m_diffProcess = new KompareProcess( m_diffSettings, Kompare::Custom, m_source, m_destination );
+ m_diffProcess->setEncoding( m_encoding );
+
+ connect( m_diffProcess, SIGNAL(diffHasFinished( bool )),
+ this, SLOT(slotDiffProcessFinished( bool )) );
+
+ emit status( Kompare::RunningDiff );
+ m_diffProcess->start();
+
+ return true;
+}
+
+bool KompareModelList::compareDirs( const QString& source, const QString& destination )
+{
+ m_source = source;
+ m_destination = destination;
+
+ clear(); // Destroy the old models...
+
+// m_dirWatch = new KDirWatch( this, "dirwatch" );
+ // Watch files in the dirs and watch the dirs recursively
+// m_dirWatch->addDir( m_source, true, true );
+// m_dirWatch->addDir( m_destination, true, true );
+
+// connect( m_dirWatch, SIGNAL( dirty ( const QString& ) ), this, SLOT( slotDirectoryChanged( const QString& ) ) );
+// connect( m_dirWatch, SIGNAL( created( const QString& ) ), this, SLOT( slotDirectoryChanged( const QString& ) ) );
+// connect( m_dirWatch, SIGNAL( deleted( const QString& ) ), this, SLOT( slotDirectoryChanged( const QString& ) ) );
+
+// m_dirWatch->startScan();
+ m_diffProcess = new KompareProcess( m_diffSettings, Kompare::Custom, m_source, m_destination );
+ m_diffProcess->setEncoding( m_encoding );
+
+ connect( m_diffProcess, SIGNAL(diffHasFinished( bool )),
+ this, SLOT(slotDiffProcessFinished( bool )) );
+
+ emit status( Kompare::RunningDiff );
+ m_diffProcess->start();
+
+ return true;
+}
+
+bool KompareModelList::openFileAndDiff( const QString& file, const QString& diff )
+{
+ clear();
+
+ if ( parseDiffOutput( readFile( diff ) ) != 0 )
+ {
+ emit error( i18n( "<qt>No models or no differences, this file: <b>%1</b>, is not a valid diff file.</qt>" ).arg( diff ) );
+ return false;
+ }
+
+ // Do our thing :)
+ if ( !blendOriginalIntoModelList( file ) )
+ {
+ kdDebug(8101) << "Oops cant blend original file into modellist : " << file << endl;
+ emit( i18n( "<qt>There were problems applying the diff <b>%1</b> to the file <b>%2</b>.</qt>" ).arg( diff ).arg( file ) );
+ return false;
+ }
+
+ updateModelListActions();
+ show();
+
+ return true;
+}
+
+bool KompareModelList::openDirAndDiff( const QString& dir, const QString& diff )
+{
+ clear();
+
+ if ( parseDiffOutput( readFile( diff ) ) != 0 )
+ {
+ emit error( i18n( "<qt>No models or no differences, this file: <b>%1</b>, is not a valid diff file.</qt>" ).arg( diff ) );
+ return false;
+ }
+
+ // Do our thing :)
+ if ( !blendOriginalIntoModelList( dir ) )
+ {
+ // Trouble blending the original into the model
+ kdDebug(8101) << "Oops cant blend original dir into modellist : " << dir << endl;
+ emit error( i18n( "<qt>There were problems applying the diff <b>%1</b> to the folder <b>%2</b>.</qt>" ).arg( diff ).arg( dir ) );
+ return false;
+ }
+
+ updateModelListActions();
+ show();
+
+ return true;
+}
+
+void KompareModelList::slotSaveDestination()
+{
+ if ( m_selectedModel )
+ {
+ saveDestination( m_selectedModel );
+ }
+}
+
+bool KompareModelList::saveDestination( DiffModel* model )
+{
+ kdDebug() << "KompareModelList::saveDestination: " << endl;
+
+ if( !model->isModified() )
+ return true;
+
+ KTempFile* temp = new KTempFile();
+
+ if( temp->status() != 0 ) {
+ emit error( i18n( "Could not open a temporary file." ) );
+ temp->unlink();
+ delete temp;
+ return false;
+ }
+
+ QTextStream* stream = temp->textStream();
+ QStringList list;
+
+ DiffHunkListConstIterator hunkIt = model->hunks()->begin();
+ DiffHunkListConstIterator hEnd = model->hunks()->end();
+
+ for( ; hunkIt != hEnd; ++hunkIt )
+ {
+ DiffHunk* hunk = *hunkIt;
+
+ DifferenceListConstIterator diffIt = hunk->differences().begin();
+ DifferenceListConstIterator dEnd = hunk->differences().end();
+
+ 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() );
+ }
+ }
+ }
+ }
+
+ // kdDebug( 8101 ) << "Everything: " << endl << list.join( "\n" ) << endl;
+
+ if( list.count() > 0 )
+ *stream << list.join( "" );
+
+ temp->close();
+ if( temp->status() != 0 ) {
+ emit error( i18n( "<qt>Could not write to the temporary file <b>%1</b>, deleting it.</qt>" ).arg( temp->name() ) );
+ temp->unlink();
+ delete temp;
+ return false;
+ }
+
+ bool result = false;
+
+ if ( m_info.mode == Kompare::ComparingDirs )
+ {
+ QString destination = model->destinationPath() + model->destinationFile();
+ kdDebug(8101) << "Tempfilename : " << temp->name() << endl;
+ kdDebug(8101) << "DestinationURL : " << destination << endl;
+ KIO::UDSEntry entry;
+ if ( !KIO::NetAccess::stat( KURL( destination ).path(), entry, (QWidget*)parent() ) )
+ {
+ if ( !KIO::NetAccess::mkdir( KURL( destination ).path(), (QWidget*)parent() ) )
+ {
+ emit error( i18n( "<qt>Could not create destination directory <b>%1</b>.\nThe file has not been saved.</qt>" ) );
+ return false;
+ }
+ }
+ result = KIO::NetAccess::upload( temp->name(), KURL( destination ), (QWidget*)parent() );
+ }
+ else
+ {
+ kdDebug(8101) << "Tempfilename : " << temp->name() << endl;
+ kdDebug(8101) << "DestinationURL : " << m_destination << endl;
+ result = KIO::NetAccess::upload( temp->name(), KURL( m_destination ), (QWidget*)parent() );
+ }
+
+ if ( !result )
+ {
+ emit error( i18n( "<qt>Could not upload the temporary file to the destination location <b>%1</b>. The temporary file is still available under: <b>%2</b>. You can manually copy it to the right place.</qt>" ).arg( m_destination ).arg( temp->name() ) );
+ }
+ else
+ {
+ //model->slotSetModified( false );
+ temp->unlink();
+ delete temp;
+ }
+
+ return true;
+}
+
+bool KompareModelList::saveAll()
+{
+ if ( !m_models )
+ 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.lower() == "default" )
+ {
+ m_textCodec = QTextCodec::codecForLocale();
+ }
+ else
+ {
+ kdDebug() << "Encoding : " << encoding << endl;
+ m_textCodec = KGlobal::charsets()->codecForName( encoding.latin1() );
+ kdDebug() << "TextCodec: " << m_textCodec << endl;
+ if ( !m_textCodec )
+ m_textCodec = QTextCodec::codecForLocale();
+ }
+ kdDebug() << "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 )
+ {
+ kdDebug() << "Blend this crap please and dont gimme 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() );
+ }
+
+ delete m_diffProcess;
+ m_diffProcess = 0;
+}
+
+void KompareModelList::slotDirectoryChanged( const QString& /*dir*/ )
+{
+ // some debug output to see if watching works properly
+ kdDebug(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
+ kdDebug(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;
+ unsigned 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.find( 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( IO_ReadOnly );
+
+ QTextStream stream( &file );
+ kdDebug() << "Codec = " << m_textCodec << endl;
+
+ if ( !m_textCodec )
+ m_textCodec = QTextCodec::codecForLocale();
+
+ stream.setCodec( m_textCodec );
+
+ QString contents = stream.read();
+
+ file.close();
+
+ return contents;
+}
+
+bool KompareModelList::openDiff( const QString& diffFile )
+{
+ kdDebug(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;
+}
+
+QString KompareModelList::recreateDiff() const
+{
+ QString diff;
+
+ DiffModelListConstIterator modelIt = m_models->begin();
+ DiffModelListConstIterator mEnd = m_models->end();
+
+ for ( ; modelIt != mEnd; ++modelIt )
+ {
+ diff += (*modelIt)->recreateDiff();
+ }
+ return diff;
+}
+
+bool KompareModelList::saveDiff( const QString& url, QString directory, DiffSettings* diffSettings )
+{
+ kdDebug() << "KompareModelList::saveDiff: " << endl;
+
+ m_diffTemp = new KTempFile();
+ m_diffURL = url;
+
+ if( m_diffTemp->status() != 0 ) {
+ emit error( i18n( "Could not open a temporary file." ) );
+ m_diffTemp->unlink();
+ delete m_diffTemp;
+ m_diffTemp = 0;
+ return false;
+ }
+
+ m_diffProcess = new KompareProcess( diffSettings, Kompare::Custom, m_source, m_destination, directory );
+ m_diffProcess->setEncoding( m_encoding );
+
+ connect( m_diffProcess, SIGNAL(diffHasFinished( bool )),
+ this, SLOT(slotWriteDiffOutput( bool )) );
+
+ emit status( Kompare::RunningDiff );
+ return m_diffProcess->start();
+}
+
+void KompareModelList::slotWriteDiffOutput( bool success )
+{
+ kdDebug(8101) << "Success = " << success << endl;
+
+ if( success )
+ {
+ QTextStream* stream = m_diffTemp->textStream();
+
+ *stream << m_diffProcess->diffOutput();
+
+ m_diffTemp->close();
+
+ if( m_diffTemp->status() != 0 )
+ {
+ emit error( i18n( "Could not write to the temporary file." ) );
+ }
+
+ KIO::NetAccess::upload( m_diffTemp->name(), KURL( m_diffURL ), (QWidget*)parent() );
+
+ emit status( Kompare::FinishedWritingDiff );
+ }
+
+ m_diffURL.truncate( 0 );
+ m_diffTemp->unlink();
+
+ 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
+ kdDebug(8101) << "KompareModelList::slotSelectionChanged( " << model << ", " << diff << " )" << endl;
+ kdDebug(8101) << "Sender is : " << sender()->className() << endl;
+// kdDebug(8101) << kdBacktrace() << endl;
+
+ m_selectedModel = const_cast<DiffModel*>(model);
+ m_modelIndex = m_models->findIndex( m_selectedModel );
+ kdDebug( 8101 ) << "m_modelIndex = " << m_modelIndex << endl;
+ m_selectedDifference = const_cast<Difference*>(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
+ kdDebug(8101) << "KompareModelList::slotSelectionChanged( " << diff << " )" << endl;
+ kdDebug(8101) << "Sender is : " << sender()->className() << endl;
+
+ m_selectedDifference = const_cast<Difference*>(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()
+{
+ kdDebug( 8101 ) << "KompareModelList::firstModel()" << endl;
+ m_modelIndex = 0;
+ kdDebug( 8101 ) << "m_modelIndex = " << m_modelIndex << endl;
+
+ m_selectedModel = m_models->first();
+
+ return m_selectedModel;
+}
+
+DiffModel* KompareModelList::lastModel()
+{
+ kdDebug( 8101 ) << "KompareModelList::lastModel()" << endl;
+ m_modelIndex = m_models->count() - 1;
+ kdDebug( 8101 ) << "m_modelIndex = " << m_modelIndex << endl;
+
+ m_selectedModel = m_models->last();
+
+ return m_selectedModel;
+}
+
+DiffModel* KompareModelList::prevModel()
+{
+ kdDebug( 8101 ) << "KompareModelList::prevModel()" << endl;
+ if ( --m_modelIndex < m_models->count() )
+ {
+ kdDebug( 8101 ) << "m_modelIndex = " << m_modelIndex << endl;
+ m_selectedModel = (*m_models)[ m_modelIndex ];
+ }
+ else
+ {
+ m_selectedModel = 0;
+ m_modelIndex = 0;
+ kdDebug( 8101 ) << "m_modelIndex = " << m_modelIndex << endl;
+ }
+
+ return m_selectedModel;
+}
+
+DiffModel* KompareModelList::nextModel()
+{
+ kdDebug( 8101 ) << "KompareModelList::nextModel()" << endl;
+ if ( ++m_modelIndex < m_models->count() )
+ {
+ kdDebug( 8101 ) << "m_modelIndex = " << m_modelIndex << endl;
+ m_selectedModel = (*m_models)[ m_modelIndex ];
+ }
+ else
+ {
+ m_selectedModel = 0;
+ m_modelIndex = 0;
+ kdDebug( 8101 ) << "m_modelIndex = " << m_modelIndex << endl;
+ }
+
+ return m_selectedModel;
+}
+
+void KompareModelList::slotPreviousDifference()
+{
+ kdDebug(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;
+ }
+
+ kdDebug(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;
+ }
+
+
+ kdDebug(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()
+{
+ kdDebug(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;
+ }
+
+ kdDebug(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;
+ }
+
+ kdDebug(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 )
+{
+ kdDebug(8101) << "KompareModelList::parseDiffOutput" << endl;
+
+ QStringList diffLines = split( diff );
+
+ Parser* parser = new Parser( this );
+ m_models = parser->parse( diffLines );
+
+ m_info.generator = parser->generator();
+ m_info.format = parser->format();
+
+ delete parser;
+
+ if ( m_models )
+ {
+ m_selectedModel = firstModel();
+ kdDebug(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...
+ kdDebug(8101) << "Now i'll be damned, there should be models here !!!" << endl;
+ return -1;
+ }
+
+ return 0;
+}
+
+bool KompareModelList::blendOriginalIntoModelList( const QString& localURL )
+{
+ kdDebug() << "Hurrah we are blending..." << endl;
+ QFileInfo fi( localURL );
+
+ bool result = false;
+ DiffModel* model;
+
+ QString fileContents;
+
+ if ( fi.isDir() )
+ { // is a dir
+ kdDebug() << "Blend Dir" << endl;
+// QDir dir( localURL, QString::null, QDir::Name|QDir::DirsFirst, QDir::All );
+ DiffModelListIterator modelIt = m_models->begin();
+ DiffModelListIterator mEnd = m_models->end();
+ for ( ; modelIt != mEnd; ++modelIt )
+ {
+ model = *modelIt;
+ kdDebug(8101) << "Model : " << model << endl;
+ QString filename = model->sourcePath() + model->sourceFile();
+ if ( !filename.startsWith( localURL ) )
+ filename.prepend( localURL );
+ QFileInfo fi2( filename );
+ if ( fi2.exists() )
+ {
+ kdDebug(8101) << "Reading from: " << filename << endl;
+ fileContents = readFile( filename );
+ result = blendFile( model, fileContents );
+ }
+ else
+ {
+ kdDebug(8101) << "File " << filename << " does not exist !" << endl;
+ kdDebug(8101) << "Assume empty file !" << endl;
+ fileContents.truncate( 0 );
+ result = blendFile( model, fileContents );
+ }
+ }
+ kdDebug() << "End of Blend Dir" << endl;
+ }
+ else if ( fi.isFile() )
+ { // is a file
+ kdDebug() << "Blend File" << endl;
+ kdDebug(8101) << "Reading from: " << localURL << endl;
+ fileContents = readFile( localURL );
+
+ result = blendFile( (*m_models)[ 0 ], fileContents );
+ kdDebug() << "End of Blend File" << endl;
+ }
+
+ return result;
+}
+
+bool KompareModelList::blendFile( DiffModel* model, const QString& fileContents )
+{
+ if ( !model )
+ {
+ kdDebug() << "**** model is null :(" << endl;
+ return false;
+ }
+
+ model->setBlended( true );
+
+ int srcLineNo = 1, destLineNo = 1;
+
+ QStringList lines = split( fileContents );
+
+ QStringList::ConstIterator linesIt = lines.begin();
+ QStringList::ConstIterator lEnd = lines.end();
+
+ DiffHunkList* hunks = model->hunks();
+ kdDebug(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 );
+
+ 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 += (*hunkIt)->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();
+ kdDebug() << "Number of differences in hunkDiffList = " << diffList.count() << endl;
+
+ DifferenceListIterator tempIt;
+ Difference* diff;
+
+ for ( ; diffIt != dEnd; ++diffIt )
+ {
+ diff = *diffIt;
+ kdDebug() << "*(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 )
+ {
+// kdDebug(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:
+ kdDebug(8101) << "Unchanged" << endl;
+ for ( int i = 0; i < diff->sourceLineCount(); i++ )
+ {
+ if ( linesIt != lEnd && *linesIt != diff->sourceLineAt( i )->string() )
+ {
+ kdDebug(8101) << "Conflict: SourceLine = " << srcLineNo << ": " << *linesIt << endl;
+ kdDebug(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 );
+ }
+// kdDebug(8101) << "SourceLine = " << srcLineNo << ": " << *linesIt << endl;
+// kdDebug(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:
+ kdDebug(8101) << "Change" << endl;
+
+ //QStringListConstIterator saveIt = linesIt;
+
+ for ( int i = 0; i < diff->sourceLineCount(); i++ )
+ {
+ if ( linesIt != lEnd && *linesIt != diff->sourceLineAt( i )->string() )
+ {
+ kdDebug(8101) << "Conflict: SourceLine = " << srcLineNo << ": " << *linesIt << endl;
+ kdDebug(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:
+ kdDebug(8101) << "Insert" << endl;
+ destLineNo += diff->destinationLineCount();
+ tempIt = diffIt;
+ --diffIt;
+ diffList.remove( tempIt );
+ newHunk->add( diff );
+ newModel->addDiff( diff );
+ break;
+ case Difference::Delete:
+ kdDebug(8101) << "Delete" << endl;
+ kdDebug(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() )
+ {
+ kdDebug(8101) << "Conflict: SourceLine = " << srcLineNo << ": " << *linesIt << endl;
+ kdDebug(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 );
+ }
+
+// kdDebug(8101) << "SourceLine = " << srcLineNo << ": " << *it << endl;
+// kdDebug(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:
+ kdDebug(8101) << "****, some diff type we dont know about ???" << endl;
+ }
+ }
+ }
+#endif
+
+/*
+ diffList = newModel->differences();
+
+ diff = diffList.first();
+ kdDebug(8101) << "Count = " << diffList.count() << endl;
+ for ( diff = diffList.first(); diff; diff = diffList.next() )
+ {
+ kdDebug(8101) << "sourcelinenumber = " << diff->sourceLineNumber() << endl;
+ }
+*/
+
+ m_selectedModel = firstModel();
+
+ m_selectedDifference = m_selectedModel->firstDifference();
+
+ return true;
+}
+
+void KompareModelList::show()
+{
+ kdDebug() << "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::swap()
+{
+ QString source = m_source;
+ QString destination = m_destination;
+ if ( m_info.mode == Kompare::ComparingFiles )
+ compareFiles( destination, source );
+ else if ( m_info.mode == Kompare::ComparingDirs )
+ compareDirs( destination, source );
+}
+
+bool KompareModelList::isModified() const
+{
+ if ( m_noOfModified > 0 )
+ 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::slotSetModified( bool modified )
+{
+ kdDebug(8101) << "KompareModelList::slotSetModified( " << modified << " );" << endl;
+ kdDebug(8101) << "Before: m_noOfModified = " << m_noOfModified << endl;
+
+ // If selectedModel emits its signal setModified it does not set the model
+ // internal m_modified bool yet, it only does that after the emit.
+ if ( modified && !m_selectedModel->isModified() )
+ m_noOfModified++;
+ else if ( !modified && m_selectedModel->isModified() )
+ m_noOfModified--;
+
+ kdDebug(8101) << "After : m_noOfModified = " << m_noOfModified << endl;
+
+ if ( m_noOfModified < 0 )
+ {
+ kdDebug(8101) << "Wow something is ****ed up..." << endl;
+ }
+ else if ( m_noOfModified == 0 )
+ {
+ emit setModified( false );
+ }
+ else // > 0 :-)
+ {
+ emit setModified( true );
+ }
+}
+
+bool KompareModelList::setSelectedModel( DiffModel* model )
+{
+ kdDebug(8101) << "KompareModelList::setSelectedModel( " << model << " )" << endl;
+
+ if ( model != m_selectedModel )
+ {
+ if ( m_models->findIndex( model ) == -1 )
+ return false;
+ kdDebug(8101) << "m_selectedModel (was) = " << m_selectedModel << endl;
+ m_modelIndex = m_models->findIndex( model );
+ kdDebug(8101) << "m_selectedModel (is) = " << m_selectedModel << endl;
+ m_selectedModel = model;
+ }
+
+ updateModelListActions();
+
+ return true;
+}
+
+void KompareModelList::updateModelListActions()
+{
+ if ( m_models && m_selectedModel && m_selectedDifference )
+ {
+ if ( ( ( KomparePart* )parent() )->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( true );
+ m_unApplyDifference->setEnabled( true );
+ m_save->setEnabled( m_selectedModel->isModified() );
+ }
+ 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
+{
+ kdDebug(8101) << "KompareModelList::hasPrevModel()" << endl;
+
+ if ( m_modelIndex > 0 )
+ {
+// kdDebug(8101) << "has prev model" << endl;
+ return true;
+ }
+
+// kdDebug(8101) << "doesn't have a prev model, this is the first one..." << endl;
+
+ return false;
+}
+
+bool KompareModelList::hasNextModel() const
+{
+ kdDebug(8101) << "KompareModelList::hasNextModel()" << endl;
+
+ if ( ( unsigned int )m_modelIndex < ( m_models->count() - 1 ) )
+ {
+// kdDebug(8101) << "has next model" << endl;
+ return true;
+ }
+
+// kdDebug(8101) << "doesn't have a next model, this is the last one..." << endl;
+ return false;
+}
+
+bool KompareModelList::hasPrevDiff() const
+{
+// kdDebug(8101) << "KompareModelList::hasPrevDiff()" << endl;
+ int index = m_selectedModel->diffIndex();
+
+ if ( index > 0 )
+ {
+// kdDebug(8101) << "has prev difference in same model" << endl;
+ return true;
+ }
+
+ if ( hasPrevModel() )
+ {
+// kdDebug(8101) << "has prev difference but in prev model" << endl;
+ return true;
+ }
+
+// kdDebug(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
+{
+// kdDebug(8101) << "KompareModelList::hasNextDiff()" << endl;
+ int index = m_selectedModel->diffIndex();
+
+ if ( index < ( m_selectedModel->differenceCount() - 1 ) )
+ {
+// kdDebug(8101) << "has next difference in same model" << endl;
+ return true;
+ }
+
+ if ( hasNextModel() )
+ {
+// kdDebug(8101) << "has next difference but in next model" << endl;
+ return true;
+ }
+
+// kdDebug(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/kompare/libdiff2/komparemodellist.h b/kompare/libdiff2/komparemodellist.h
new file mode 100644
index 00000000..8ba264fb
--- /dev/null
+++ b/kompare/libdiff2/komparemodellist.h
@@ -0,0 +1,213 @@
+/***************************************************************************
+ komparemodellist.h - description
+ -------------------
+ begin : Tue Jun 26 2001
+ copyright : (C) 2001-2003 by John Firebaugh
+ and Otto Bruggeman
+ email : jfirebaugh@kde.org
+ otto.bruggeman@home.nl
+ ***************************************************************************/
+
+/***************************************************************************
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the 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 <qobject.h>
+
+#include "diffmodel.h"
+#include "diffmodellist.h"
+#include "kompare.h"
+
+class QFile;
+
+class KAction;
+class KDirWatch;
+class KTempFile;
+
+class DiffSettings;
+class KompareProcess;
+
+namespace Diff2
+{
+
+class KompareModelList : public QObject
+{
+ Q_OBJECT
+public:
+ KompareModelList( DiffSettings* diffSettings, struct Kompare::Info& info, QObject* parent = 0, const char* name = 0 );
+ ~KompareModelList();
+
+public:
+ // Swap source with destination and show differences
+ void swap();
+
+ /* Comparing methods */
+ bool compare( const QString& source, const QString& destination );
+
+ bool compareFiles( const QString& source, const QString& destination );
+ bool compareDirs( const QString& source, const QString& destination );
+
+ bool openDiff( const QString& diff );
+
+ bool openFileAndDiff( const QString& file, const QString& diff );
+ bool openDirAndDiff( const QString& dir, const QString& diff );
+
+ 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 );
+
+ // 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 );
+
+ enum Kompare::Mode mode() const { return m_info.mode; };
+ const DiffModelList* models() const { return m_models; };
+
+ int modelCount() const;
+ int differenceCount() const;
+ int appliedCount() const;
+
+ const DiffModel* modelAt( int i ) const { return *( m_models->at( i ) ); };
+ int findModel( DiffModel* model ) const { return m_models->findIndex( model ); };
+
+ bool isModified() const;
+
+ int currentModel() const { return m_models->findIndex( 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 );
+
+ // Emits true when m_noOfModified > 0, false when m_noOfModified == 0
+ void setModified( bool modified );
+
+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();
+
+ // This slot is called by the diffmodels whenever their status changes to modified or unmodified
+ void slotSetModified( bool modified );
+
+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 );
+
+private:
+ KTempFile* m_diffTemp;
+ QString m_diffURL;
+
+ KompareProcess* m_diffProcess;
+
+ DiffSettings* m_diffSettings;
+
+ DiffModelList* m_models;
+
+ QString m_source;
+ QString m_destination;
+
+ DiffModel* m_selectedModel;
+ Difference* m_selectedDifference;
+
+ KDirWatch* m_dirWatch;
+ KDirWatch* m_fileWatch;
+
+ int m_noOfModified;
+ unsigned int m_modelIndex;
+
+ struct Kompare::Info& m_info;
+
+ 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;
+};
+
+} // End of namespace Diff2
+
+#endif
diff --git a/kompare/libdiff2/kompareprocess.cpp b/kompare/libdiff2/kompareprocess.cpp
new file mode 100644
index 00000000..2d5eac00
--- /dev/null
+++ b/kompare/libdiff2/kompareprocess.cpp
@@ -0,0 +1,269 @@
+/***************************************************************************
+ kompareprocess.cpp - description
+ -------------------
+ begin : Sun Mar 4 2001
+ copyright : (C) 2001-2003 by Otto Bruggeman
+ and John Firebaugh
+ (C) 2007 Kevin Kofler
+ email : otto.bruggeman@home.nl
+ jfirebaugh@kde.org
+ kevin.kofler@chello.at
+****************************************************************************/
+
+/***************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+***************************************************************************/
+
+#include <qdir.h>
+#include <qstringlist.h>
+#include <qtextcodec.h>
+
+#include <kcharsets.h>
+#include <kdebug.h>
+#include <kglobal.h>
+
+#include "diffsettings.h"
+#include "kompareprocess.h"
+
+KompareProcess::KompareProcess( DiffSettings* diffSettings, enum Kompare::DiffMode mode, QString source, QString destination, QString dir )
+ : KProcess(),
+ m_diffSettings( diffSettings ),
+ m_mode( mode ),
+ m_textDecoder( 0 )
+{
+ setUseShell( true );
+
+ // connect the stdout and stderr signals
+ connect( this, SIGNAL( receivedStdout( KProcess*, char*, int ) ),
+ SLOT ( slotReceivedStdout( KProcess*, char*, int ) ) );
+ connect( this, SIGNAL( receivedStderr( KProcess*, char*, int ) ),
+ SLOT ( slotReceivedStderr( KProcess*, char*, int ) ) );
+
+ // connect the signal that indicates that the proces has exited
+ connect( this, SIGNAL( processExited( KProcess* ) ),
+ SLOT ( slotProcessExited( KProcess* ) ) );
+
+ *this << "LANG=C";
+
+ // Write command and options
+ if( m_mode == Kompare::Default )
+ {
+ writeDefaultCommandLine();
+ }
+ else
+ {
+ writeCommandLine();
+ }
+
+ if( !dir.isEmpty() ) {
+ QDir::setCurrent( dir );
+ }
+
+ // Write file names
+ *this << "--";
+ *this << KProcess::quote( constructRelativePath( dir, source ) );
+ *this << KProcess::quote( 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() )
+ {
+ kdDebug(8101) << "Using the first diff in the path..." << endl;
+ *this << "diff";
+ }
+ else
+ {
+ kdDebug(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 )
+ {
+ *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 " << KProcess::quote( 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 )
+ {
+ QStringList::ConstIterator it = m_diffSettings->m_excludeFilePatternList.begin();
+ QStringList::ConstIterator end = m_diffSettings->m_excludeFilePatternList.end();
+ for ( ; it != end; ++it )
+ {
+ *this << "-x" << KProcess::quote( *it );
+ }
+ }
+
+ if ( m_diffSettings->m_excludeFilesFile && !m_diffSettings->m_excludeFilesFileURL.isEmpty() )
+ {
+ *this << "-X" << KProcess::quote( m_diffSettings->m_excludeFilesFileURL );
+ }
+}
+
+KompareProcess::~KompareProcess()
+{
+}
+
+void KompareProcess::setEncoding( const QString& encoding )
+{
+ if ( encoding.lower() == "default" )
+ {
+ m_textDecoder = QTextCodec::codecForLocale()->makeDecoder();
+ }
+ else
+ {
+ QTextCodec* textCodec = KGlobal::charsets()->codecForName( encoding.latin1() );
+ if ( textCodec )
+ m_textDecoder = textCodec->makeDecoder();
+ else
+ {
+ kdDebug(8101) << "Using locale codec as backup..." << endl;
+ textCodec = QTextCodec::codecForLocale();
+ m_textDecoder = textCodec->makeDecoder();
+ }
+ }
+}
+
+void KompareProcess::slotReceivedStdout( KProcess* /* process */, char* buffer, int length )
+{
+ // add all output to m_stdout
+ if ( m_textDecoder )
+ m_stdout += m_textDecoder->toUnicode( buffer, length );
+ else
+ kdDebug(8101) << "KompareProcess::slotReceivedStdout : No decoder !!!" << endl;
+}
+
+void KompareProcess::slotReceivedStderr( KProcess* /* process */, char* buffer, int length )
+{
+ // add all output to m_stderr
+ if ( m_textDecoder )
+ m_stderr += m_textDecoder->toUnicode( buffer, length );
+ else
+ kdDebug(8101) << "KompareProcess::slotReceivedStderr : No decoder !!!" << endl;
+}
+
+bool KompareProcess::start()
+{
+#ifndef NDEBUG
+ QString cmdLine;
+ QValueList<QCString>::ConstIterator it = arguments.begin();
+ for (; it != arguments.end(); ++it )
+ cmdLine += "\"" + (*it) + "\" ";
+ kdDebug(8101) << cmdLine << endl;
+#endif
+ return( KProcess::start( KProcess::NotifyOnExit, KProcess::AllOutput ) );
+}
+
+void KompareProcess::slotProcessExited( KProcess* /* proc */ )
+{
+ // exit status of 0: no differences
+ // 1: some differences
+ // 2: error but there may be differences !
+ kdDebug(8101) << "Exited with exit status : " << exitStatus() << endl;
+ emit diffHasFinished( normalExit() && exitStatus() != 0 );
+}
+
+#include "kompareprocess.moc"
+
diff --git a/kompare/libdiff2/kompareprocess.h b/kompare/libdiff2/kompareprocess.h
new file mode 100644
index 00000000..06a4a5ce
--- /dev/null
+++ b/kompare/libdiff2/kompareprocess.h
@@ -0,0 +1,67 @@
+/***************************************************************************
+ kompareprocess.h - description
+ -------------------
+ begin : Sun Mar 4 2001
+ copyright : (C) 2001-2003 by Otto Bruggeman
+ and John Firebaugh
+ email : otto.bruggeman@home.nl
+ jfirebaugh@kde.org
+****************************************************************************/
+
+/***************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+***************************************************************************/
+
+#ifndef KOMPAREPROCESS_H
+#define KOMPAREPROCESS_H
+
+#include <kprocess.h>
+
+#include "kompare.h"
+
+class QTextCodec;
+
+class DiffSettings;
+
+class KompareProcess : public KProcess, public KompareFunctions
+{
+ Q_OBJECT
+
+public:
+ KompareProcess( DiffSettings* diffSettings, enum Kompare::DiffMode mode, QString source, QString destination, QString directory = QString::null );
+ ~KompareProcess();
+
+ bool 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 slotReceivedStdout( KProcess*, char*, int );
+ void slotReceivedStderr( KProcess*, char*, int );
+ void slotProcessExited( KProcess* proc );
+
+private:
+ DiffSettings* m_diffSettings;
+ enum Kompare::DiffMode m_mode;
+ QString m_stdout;
+ QString m_stderr;
+ QTextDecoder* m_textDecoder;
+};
+
+#endif
diff --git a/kompare/libdiff2/levenshteintable.cpp b/kompare/libdiff2/levenshteintable.cpp
new file mode 100644
index 00000000..54525aef
--- /dev/null
+++ b/kompare/libdiff2/levenshteintable.cpp
@@ -0,0 +1,332 @@
+/*******************************************************************************
+**
+** Filename : levenshteintable.cpp
+** Created on : 08 november, 2003
+** Copyright : (c) 2003 Otto Bruggeman
+** Email : bruggie@home.nl
+**
+*******************************************************************************/
+
+/*******************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the 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 <iostream>
+
+#include <qstring.h>
+
+#include <kdebug.h>
+#include <kglobal.h>
+
+#include "levenshteintable.h"
+
+#include "difference.h"
+
+using namespace Diff2;
+
+LevenshteinTable::LevenshteinTable()
+ : m_width( 256 ),
+ m_height( 256 ),
+ m_size( m_height * m_width ),
+ m_table( new unsigned int[ m_size ] ),
+ m_source( 0 ),
+ m_destination( 0 )
+{
+}
+
+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_source( 0 ),
+ m_destination( 0 )
+{
+}
+
+LevenshteinTable::~LevenshteinTable()
+{
+ delete[] m_table;
+ m_source = 0;
+ m_destination = 0;
+}
+
+int LevenshteinTable::getContent( unsigned int posX, unsigned int posY ) const
+{
+// kdDebug(8101) << "Width = " << m_width << ", height = " << m_height << ", posX = " << posX << ", posY = " << posY << endl;
+ return m_table[ posY * m_width + posX ];
+}
+
+int LevenshteinTable::setContent( unsigned int posX, unsigned int posY, int value )
+{
+ m_table[ posY * m_width + posX ] = value;
+
+ return 0;
+}
+
+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;
+}
+
+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;
+ }
+}
+
+unsigned int LevenshteinTable::createTable( DifferenceString* source, DifferenceString* destination )
+{
+ m_source = source;
+ m_destination = destination;
+
+ QString s = ' ' + source->string(); // Optimization, so i dont have to subtract 1 from the indexes every
+ QString d = ' ' + destination->string(); // single time and add 1 to the width and height of the table
+
+ unsigned int m = s.length();
+ unsigned int n = d.length();
+
+ const QChar* sq = s.unicode();
+ const QChar* dq = d.unicode();
+
+ if ( m == 1 )
+ return --n;
+
+ if ( n == 1 )
+ return --m;
+
+ 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;
+
+ ushort 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 )
+ {
+ dj = dq[ j ];
+
+ for ( i = 1; i < m; ++i )
+ {
+ si = sq[ i ];
+ if ( si == dj )
+ cost = 0;
+ else
+ cost = 1;
+
+ north = getContent( i, j-1 ) + 1;
+ west = getContent( i-1, j ) + 1;
+ northwest = getContent( i-1, j-1 ) + cost;
+
+ setContent( i, j, kMin( north, kMin( west, northwest ) ) );
+ }
+ }
+
+ return getContent( m-1, n-1 );
+}
+
+int LevenshteinTable::chooseRoute( int c1, int c2, int c3 )
+{
+// kdDebug(8101) << "c1 = " << c1 << ", c2 = " << c2 << ", c3 = " << c3 << endl;
+ // preference order: c2, c3, c1, hopefully this will work out for me
+ if ( c2 <= c1 && c2 <= c3 )
+ return 1;
+
+ if ( c3 <= c2 && c3 <= c1 )
+ return 2;
+
+ return 0;
+}
+
+void LevenshteinTable::createListsOfMarkers()
+{
+// std::cout << source.latin1() << std::endl;
+// std::cout << destination.latin1() << std::endl;
+// dumpLevenshteinTable();
+
+ unsigned int x = m_width-1;
+ unsigned int y = m_height-1;
+
+ Marker* c = 0;
+
+ int n, nw, w, direction, currentValue;
+ while ( x > 0 && y > 0 )
+ {
+ currentValue = getContent( x, y );
+
+ nw = getContent( x - 1, y - 1 );
+ n = getContent( x, y - 1 );
+ w = getContent( x - 1, y );
+
+ direction = chooseRoute( n, nw, w );
+
+ switch ( direction )
+ {
+ case 0: // north
+// kdDebug(8101) << "Picking north" << endl;
+// kdDebug(8101) << "Source[" << ( x - 1 ) << "] = " << QString( source[ x-1 ] ) << ", destination[" << ( y - 1 ) << "] = " << QString( destination[ y-1 ] ) << endl;
+
+ if ( !m_destination->markerList().isEmpty() )
+ c = m_destination->markerList().first();
+ else
+ c = 0;
+
+ if ( c && c->type() == Marker::End )
+ {
+// kdDebug(8101) << "CurrentValue: " << currentValue << endl;
+ if ( n == currentValue )
+ m_destination->prepend( new Marker( Marker::Start, y ) );
+ // else: the change continues, dont do anything
+ }
+ else
+ {
+// kdDebug(8101) << "CurrentValue: " << currentValue << endl;
+ if ( n < currentValue )
+ m_destination->prepend( new Marker( Marker::End, y ) );
+ }
+
+ --y;
+ break;
+ case 1: // northwest
+// kdDebug(8101) << "Picking northwest" << endl;
+// kdDebug(8101) << "Source[" << ( x - 1 ) << "] = " << QString( source[ x-1 ] ) << ", destination[" << ( y - 1 ) << "] = " << QString( destination[ y-1 ] ) << endl;
+
+ if ( !m_destination->markerList().isEmpty() )
+ c = m_destination->markerList().first();
+ else
+ c = 0;
+
+ if ( c && c->type() == Marker::End )
+ {
+// kdDebug(8101) << "End found: CurrentValue: " << currentValue << endl;
+ if ( nw == currentValue )
+ m_destination->prepend( new Marker( Marker::Start, y ) );
+ // else: the change continues, dont do anything
+ }
+ else
+ {
+// kdDebug(8101) << "CurrentValue: " << currentValue << endl;
+ if ( nw < currentValue )
+ m_destination->prepend( new Marker( Marker::End, y ) );
+ }
+
+ if ( !m_source->markerList().isEmpty() )
+ c = m_source->markerList().first();
+ else
+ c = 0;
+
+ if ( c && c->type() == Marker::End )
+ {
+// kdDebug(8101) << "End found: CurrentValue: " << currentValue << endl;
+ if ( nw == currentValue )
+ m_source->prepend( new Marker( Marker::Start, x ) );
+ // else: the change continues, dont do anything
+ }
+ else
+ {
+// kdDebug(8101) << "CurrentValue: " << currentValue << endl;
+ if ( nw < currentValue )
+ m_source->prepend( new Marker( Marker::End, x ) );
+ }
+
+ --y;
+ --x;
+ break;
+ case 2: // west
+// kdDebug(8101) << "Picking west" << endl;
+// kdDebug(8101) << "Source[" << ( x - 1 ) << "] = " << QString( source[ x-1 ] ) << ", destination[" << ( y - 1 ) << "] = " << QString( destination[ y-1 ] ) << endl;
+
+ if ( !m_source->markerList().isEmpty() )
+ c = m_source->markerList().first();
+ else
+ c = 0;
+
+ if ( c && c->type() == Marker::End )
+ {
+// kdDebug(8101) << "End found: CurrentValue: " << currentValue << endl;
+ if ( w == currentValue )
+ m_source->prepend( new Marker( Marker::Start, x ) );
+ // else: the change continues, dont do anything
+ }
+ else
+ {
+// kdDebug(8101) << "CurrentValue: " << currentValue << endl;
+ if ( w < currentValue )
+ m_source->prepend( new Marker( Marker::End, x ) );
+ }
+
+ --x;
+ break;
+ }
+ }
+
+// kdDebug(8101) << "Source string: " << m_source->string() << endl;
+// c = m_source->markerList()->first();
+// QStringList list;
+// unsigned int prevValue = 0;
+// for ( ; c; c = m_source->markerList()->next() )
+// {
+// kdDebug(8101) << "Source Marker Entry : Type: " << c->type() << ", Offset: " << c->offset() << endl;
+// list.append( m_source->string().mid( prevValue, c->offset() - prevValue ) );
+// prevValue = c->offset();
+// }
+// if ( prevValue < m_source->string().length() - 1 )
+// {
+// list.append( m_source->string().mid( prevValue, m_source->string().length() - prevValue ) );
+// }
+// kdDebug(8101) << "Source Resulting stringlist : " << list.join("\n") << endl;
+
+// list.clear();
+// prevValue = 0;
+
+// kdDebug(8101) << "Destination string: " << m_destination->string() << endl;
+// for ( ; c; c = m_destination->markerList()->next() )
+// {
+// kdDebug(8101) << "Destination Marker Entry : Type: " << c->type() << ", Offset: " << c->offset() << endl;
+// list.append( m_destination->string().mid( prevValue, c->offset() - prevValue ) );
+// prevValue = c->offset();
+// }
+// if ( prevValue < m_destination->string().length() - 1 )
+// {
+// list.append( m_destination->string().mid( prevValue, m_destination->string().length() - prevValue ) );
+// }
+// kdDebug(8101) << "Destination Resulting string : " << list.join("\n") << endl;
+}
+
diff --git a/kompare/libdiff2/levenshteintable.h b/kompare/libdiff2/levenshteintable.h
new file mode 100644
index 00000000..201d1c10
--- /dev/null
+++ b/kompare/libdiff2/levenshteintable.h
@@ -0,0 +1,69 @@
+/*******************************************************************************
+**
+** Filename : levenshteintable.h
+** Created on : 08 november, 2003
+** Copyright : (c) 2003 Otto Bruggeman
+** Email : bruggie@home.nl
+**
+*******************************************************************************/
+
+/*******************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the 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 "difference.h"
+
+class QString;
+
+namespace Diff2 {
+
+class Marker;
+
+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 will calculate the levenshtein distance of 2 strings */
+ unsigned int createTable( DifferenceString* s, DifferenceString* d );
+
+ void createListsOfMarkers( void );
+ int chooseRoute( int c1, int c2, int c3 );
+
+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;
+ DifferenceString* m_source;
+ DifferenceString* m_destination;
+};
+
+} // namespace Diff2
+
+#endif // _LEVENSHTEIN_H
diff --git a/kompare/libdiff2/parser.cpp b/kompare/libdiff2/parser.cpp
new file mode 100644
index 00000000..04ff7a4a
--- /dev/null
+++ b/kompare/libdiff2/parser.cpp
@@ -0,0 +1,139 @@
+/**************************************************************************
+** parser.cpp
+** -------------------
+** begin : Sun Aug 4 15:05:35 2002
+** copyright : (C) 2002-2004 Otto Bruggeman
+** email : otto.bruggeman@home.nl
+**
+***************************************************************************/
+/***************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the 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 <kdebug.h>
+
+#include "parser.h"
+#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.remove( it );
+ // correcting the advance of the iterator because of the remove
+ --it;
+ QString temp( *it );
+ temp.truncate( temp.find( '\n' ) );
+ *it = temp;
+ ++nol;
+ }
+ }
+
+ return nol;
+}
+
+DiffModelList* Parser::parse( QStringList& diffLines )
+{
+ /* Basically determine the generator then call the parse method */
+ ParserBase* parser;
+
+ m_generator = determineGenerator( diffLines );
+
+ int nol = cleanUpCrap( diffLines );
+ kdDebug(8101) << "Cleaned up " << nol << " line(s) of crap from the diff..." << endl;
+
+ switch( m_generator )
+ {
+ case Kompare::CVSDiff :
+ kdDebug(8101) << "It is a CVS generated diff..." << endl;
+ parser = new CVSDiffParser( m_list, diffLines );
+ break;
+ case Kompare::Diff :
+ kdDebug(8101) << "It is a diff generated diff..." << endl;
+ parser = new DiffParser( m_list, diffLines );
+ break;
+ case Kompare::Perforce :
+ kdDebug(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();
+ if ( modelList )
+ {
+ kdDebug(8101) << "Modelcount: " << modelList->count() << endl;
+ DiffModelListIterator modelIt = modelList->begin();
+ DiffModelListIterator mEnd = modelList->end();
+ for ( ; modelIt != mEnd; ++modelIt )
+ {
+ kdDebug(8101) << "Hunkcount: " << (*modelIt)->hunkCount() << endl;
+ kdDebug(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 ) )
+ {
+ kdDebug(8101) << "Diff is a CVSDiff" << endl;
+ return Kompare::CVSDiff;
+ }
+ else if ( ( *it ).startsWith( perforceDiff ) )
+ {
+ kdDebug(8101) << "Diff is a Perforce Diff" << endl;
+ return Kompare::Perforce;
+ }
+ ++it;
+ }
+
+ kdDebug(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/kompare/libdiff2/parser.h b/kompare/libdiff2/parser.h
new file mode 100644
index 00000000..0ffae23a
--- /dev/null
+++ b/kompare/libdiff2/parser.h
@@ -0,0 +1,58 @@
+/**************************************************************************
+** parser.h
+** --------
+** begin : Tue Jul 30 23:53:52 2002
+** copyright : (C) 2002-2004 Otto Bruggeman
+** email : otto.bruggeman@home.nl
+**
+***************************************************************************/
+/***************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the 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 );
+
+ 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/kompare/libdiff2/parserbase.cpp b/kompare/libdiff2/parserbase.cpp
new file mode 100644
index 00000000..303f7b22
--- /dev/null
+++ b/kompare/libdiff2/parserbase.cpp
@@ -0,0 +1,739 @@
+/**************************************************************************
+** parserbase.cpp
+** -------------------
+** begin : Sun Aug 4 15:05:35 2002
+** copyright : (C) 2002-2004 Otto Bruggeman
+** email : otto.bruggeman@home.nl
+**
+***************************************************************************/
+/***************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the 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 <qobject.h>
+
+#include <kdebug.h>
+
+#include "diffmodel.h"
+#include "diffhunk.h"
+#include "difference.h"
+#include "komparemodellist.h"
+
+#include "parserbase.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_list( list )
+{
+// kdDebug(8101) << diff << endl;
+// kdDebug(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; // dont 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()
+{
+ switch( determineFormat() )
+ {
+ case Kompare::Context :
+ return parseContext();
+ case Kompare::Ed :
+ return parseEd();
+ case Kompare::Normal :
+ return parseNormal();
+ case Kompare::RCS :
+ return parseRCS();
+ case Kompare::Unified :
+ return parseUnified();
+ default: // Unknown and SideBySide for now
+ return 0L;
+ }
+}
+
+bool ParserBase::parseContextDiffHeader()
+{
+// kdDebug(8101) << "ParserBase::parseContextDiffHeader()" << endl;
+ bool result = false;
+
+ while ( m_diffIterator != m_diffLines.end() )
+ {
+ if ( !m_contextDiffHeader1.exactMatch( *(m_diffIterator)++ ) )
+ {
+ continue;
+ }
+// kdDebug(8101) << "Matched length Header1 = " << m_contextDiffHeader1.matchedLength() << endl;
+// kdDebug(8101) << "Matched string Header1 = " << m_contextDiffHeader1.cap( 0 ) << endl;
+ if ( m_diffIterator != m_diffLines.end() && m_contextDiffHeader2.exactMatch( *m_diffIterator ) )
+ {
+// kdDebug(8101) << "Matched length Header2 = " << m_contextDiffHeader2.matchedLength() << endl;
+// kdDebug(8101) << "Matched string Header2 = " << m_contextDiffHeader2.cap( 0 ) << endl;
+
+ m_currentModel = new DiffModel( m_contextDiffHeader1.cap( 1 ), m_contextDiffHeader2.cap( 1 ) );
+ QObject::connect( m_currentModel, SIGNAL( setModified( bool ) ), m_list, SLOT( slotSetModified( bool ) ) );
+ m_currentModel->setSourceTimestamp ( m_contextDiffHeader1.cap( 2 ) );
+ m_currentModel->setSourceRevision ( m_contextDiffHeader1.cap( 4 ) );
+ m_currentModel->setDestinationTimestamp( m_contextDiffHeader2.cap( 2 ) );
+ m_currentModel->setDestinationRevision ( m_contextDiffHeader2.cap( 4 ) );
+
+ ++m_diffIterator;
+ result = true;
+
+ break;
+ }
+ else
+ {
+ // We're screwed, second line does not match or is not there...
+ break;
+ }
+ // Dont 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()
+{
+// kdDebug(8101) << "ParserBase::parseNormalDiffHeader()" << endl;
+ bool result = false;
+
+ while ( m_diffIterator != m_diffLines.end() )
+ {
+ if ( m_normalDiffHeader.exactMatch( *m_diffIterator ) )
+ {
+// kdDebug(8101) << "Matched length Header = " << m_normalDiffHeader.matchedLength() << endl;
+// kdDebug(8101) << "Matched string Header = " << m_normalDiffHeader.cap( 0 ) << endl;
+
+ m_currentModel = new DiffModel();
+ QObject::connect( m_currentModel, SIGNAL( setModified( bool ) ), m_list, SLOT( slotSetModified( bool ) ) );
+ m_currentModel->setSourceFile ( m_normalDiffHeader.cap( 1 ) );
+ m_currentModel->setDestinationFile ( m_normalDiffHeader.cap( 2 ) );
+
+ result = true;
+
+ ++m_diffIterator;
+ break;
+ }
+ else
+ {
+ kdDebug(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();
+ QObject::connect( m_currentModel, SIGNAL( setModified( bool ) ), m_list, SLOT( slotSetModified( bool ) ) );
+ m_singleFileDiff = true;
+ }
+
+ return result;
+}
+
+bool ParserBase::parseRCSDiffHeader()
+{
+ return false;
+}
+
+bool ParserBase::parseUnifiedDiffHeader()
+{
+// kdDebug(8101) << "ParserBase::parseUnifiedDiffHeader()" << endl;
+ bool result = false;
+
+ while ( m_diffIterator != m_diffLines.end() ) // dont assume we start with the diffheader1 line
+ {
+ if ( !m_unifiedDiffHeader1.exactMatch( *m_diffIterator ) )
+ {
+ ++m_diffIterator;
+ continue;
+ }
+// kdDebug(8101) << "Matched length Header1 = " << m_unifiedDiffHeader1.matchedLength() << endl;
+// kdDebug(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 ) );
+ QObject::connect( m_currentModel, SIGNAL( setModified( bool ) ), m_list, SLOT( slotSetModified( bool ) ) );
+ 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()
+{
+// kdDebug(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()
+{
+// kdDebug(8101) << "ParserBase::parseNormalHunkHeader()" << endl;
+ if ( m_diffIterator != m_diffLines.end() )
+ {
+// kdDebug(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()
+{
+// kdDebug(8101) << "ParserBase::parseUnifiedHunkHeader()" << endl;
+
+ if ( m_unifiedHunkHeader.exactMatch( *m_diffIterator ) )
+ {
+ ++m_diffIterator;
+ return true;
+ }
+ else
+ {
+// kdDebug(8101) << "This is not a unified hunk header : " << (*m_diffIterator) << endl;
+ return false;
+ }
+
+}
+
+bool ParserBase::parseContextHunkBody()
+{
+// kdDebug(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 ) {
+// kdDebug(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 ) {
+// kdDebug(8101) << "Added new line: " << *m_diffIterator << endl;
+ newLines.append( *m_diffIterator );
+ }
+
+ QString function = m_contextHunkHeader1.cap( 1 );
+// kdDebug(8101) << "Captured function: " << function << endl;
+ int linenoA = m_contextHunkHeader2.cap( 1 ).toInt();
+// kdDebug(8101) << "Source line number: " << linenoA << endl;
+ int linenoB = m_contextHunkHeader3.cap( 1 ).toInt();
+// kdDebug(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 ) )
+ {
+// kdDebug(8101) << "Delete: " << endl;
+ diff = new Difference( linenoA, linenoB );
+ diff->setType( Difference::Delete );
+ m_currentModel->addDiff( diff );
+// kdDebug(8101) << "Difference added" << endl;
+ hunk->add( diff );
+ for( ; oldIt != oldLines.end() && m_contextHunkBodyRemoved.exactMatch( *oldIt ); ++oldIt )
+ {
+// kdDebug(8101) << " " << m_contextHunkBodyRemoved.cap( 1 ) << endl;
+ diff->addSourceLine( m_contextHunkBodyRemoved.cap( 1 ) );
+ linenoA++;
+ }
+ }
+ else if( newIt != newLines.end() && m_contextHunkBodyAdded.exactMatch( *newIt ) )
+ {
+// kdDebug(8101) << "Insert: " << endl;
+ diff = new Difference( linenoA, linenoB );
+ diff->setType( Difference::Insert );
+ m_currentModel->addDiff( diff );
+// kdDebug(8101) << "Difference added" << endl;
+ hunk->add( diff );
+ for( ; newIt != newLines.end() && m_contextHunkBodyAdded.exactMatch( *newIt ); ++newIt )
+ {
+// kdDebug(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 ) ) )
+ {
+// kdDebug(8101) << "Unchanged: " << endl;
+ diff = new Difference( linenoA, linenoB );
+ // Dont 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 );
+// kdDebug(8101) << "old: " << l << endl;
+ ++oldIt;
+ }
+ if( newIt != newLines.end() )
+ {
+ l = m_contextHunkBodyContext.cap( 1 );
+// kdDebug(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 ) ) )
+ {
+// kdDebug(8101) << "Changed: " << endl;
+ diff = new Difference( linenoA, linenoB );
+ diff->setType( Difference::Change );
+ m_currentModel->addDiff( diff );
+// kdDebug(8101) << "Difference added" << endl;
+ hunk->add( diff );
+ while( oldIt != oldLines.end() && m_contextHunkBodyChanged.exactMatch( *oldIt ) )
+ {
+// kdDebug(8101) << " " << m_contextHunkBodyChanged.cap( 1 ) << endl;
+ diff->addSourceLine( m_contextHunkBodyChanged.cap( 1 ) );
+ linenoA++;
+ ++oldIt;
+ }
+ while( newIt != newLines.end() && m_contextHunkBodyChanged.exactMatch( *newIt ) )
+ {
+// kdDebug(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()
+{
+// kdDebug(8101) << "ParserBase::parseNormalHunkBody" << endl;
+
+ QString type = QString::null;
+
+ 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 )
+ {
+// kdDebug(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 ) )
+ {
+// kdDebug(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 )
+ {
+// kdDebug(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()
+{
+// kdDebug(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();
+ if( !m_unifiedHunkHeader.cap( 3 ).isEmpty() && m_unifiedHunkHeader.cap( 3 ).toInt(&wasNum) == 0 ) {
+ // 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( wasNum == false )
+ return false;
+ linenoA++;
+ }
+ linenoB = m_unifiedHunkHeader.cap( 4 ).toInt();
+ if( !m_unifiedHunkHeader.cap( 6 ).isEmpty() && m_unifiedHunkHeader.cap( 6 ).toInt(&wasNum) == 0 ) {
+ // see above
+ if( wasNum == false )
+ return false;
+ linenoB++;
+ }
+ QString function = m_unifiedHunkHeader.cap( 7 );
+ for ( int i = 0; i < 9; i++ )
+ {
+// kdDebug(8101) << "Capture " << i << ": " << m_unifiedHunkHeader.cap( i ) << endl;
+ }
+
+ 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 ) )
+ {
+ Difference* diff = new Difference( linenoA, linenoB );
+ hunk->add( diff );
+
+ if( (*m_diffIterator).startsWith( context ) )
+ { // context
+ for( ; m_diffIterator != m_diffLinesEnd && (*m_diffIterator).startsWith( context ); ++m_diffIterator )
+ {
+ diff->addSourceLine( QString( *m_diffIterator ).remove( 0, 1 ) );
+ diff->addDestinationLine( QString( *m_diffIterator ).remove( 0, 1 ) );
+ linenoA++;
+ linenoB++;
+ }
+ }
+ else
+ { // This is a real difference, not context
+ for( ; m_diffIterator != m_diffLinesEnd && (*m_diffIterator).startsWith( removed ); ++m_diffIterator )
+ {
+ diff->addSourceLine( QString( *m_diffIterator ).remove( 0, 1 ) );
+ linenoA++;
+ }
+ for( ; m_diffIterator != m_diffLinesEnd && (*m_diffIterator).startsWith( added ); ++m_diffIterator )
+ {
+ diff->addDestinationLine( QString( *m_diffIterator ).remove( 0, 1 ) );
+ linenoB++;
+ }
+ if ( diff->sourceLineCount() == 0 )
+ {
+ diff->setType( Difference::Insert );
+// kdDebug(8101) << "Insert difference" << endl;
+ }
+ else if ( diff->destinationLineCount() == 0 )
+ {
+ diff->setType( Difference::Delete );
+// kdDebug(8101) << "Delete difference" << endl;
+ }
+ else
+ {
+ diff->setType( Difference::Change );
+// kdDebug(8101) << "Change difference" << endl;
+ }
+ diff->determineInlineDifferences();
+ m_currentModel->addDiff( diff );
+ }
+ }
+
+ return true;
+}
+
+DiffModelList* ParserBase::parseContext()
+{
+ while ( parseContextDiffHeader() )
+ {
+ while ( parseContextHunkHeader() )
+ parseContextHunkBody();
+ 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::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 );
+ }
+
+ if ( m_singleFileDiff )
+ {
+ while ( parseNormalHunkHeader() )
+ parseNormalHunkBody();
+ 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::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();
+// kdDebug(8101) << "New model ready to be analyzed..." << endl;
+// kdDebug(8101) << " differenceCount() == " << m_currentModel->differenceCount() << endl;
+ 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;
+ }
+}
+
diff --git a/kompare/libdiff2/parserbase.h b/kompare/libdiff2/parserbase.h
new file mode 100644
index 00000000..5e08803e
--- /dev/null
+++ b/kompare/libdiff2/parserbase.h
@@ -0,0 +1,133 @@
+/**************************************************************************
+** parserbase.h
+** -------------------
+** begin : Tue Jul 30 23:53:52 2002
+** copyright : (C) 2002-2004 Otto Bruggeman
+** email : otto.bruggeman@home.nl
+**
+***************************************************************************/
+/***************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the 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 <qregexp.h>
+
+#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();
+
+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;
+
+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;
+
+protected:
+ const KompareModelList* m_list;
+};
+
+} // End of namespace Diff2
+
+#endif
diff --git a/kompare/libdiff2/perforceparser.cpp b/kompare/libdiff2/perforceparser.cpp
new file mode 100644
index 00000000..907d88ff
--- /dev/null
+++ b/kompare/libdiff2/perforceparser.cpp
@@ -0,0 +1,223 @@
+/**************************************************************************
+** perforceparser.cpp
+** ------------------
+** begin : Sun Aug 4 15:05:35 2002
+** copyright : (C) 2002-2004 Otto Bruggeman
+** email : otto.bruggeman@home.nl
+**
+***************************************************************************/
+/***************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the 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 <qregexp.h>
+
+#include <kdebug.h>
+
+#include "perforceparser.h"
+
+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()
+{
+ kdDebug(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).find( unifiedRE, 0 ) == 0 )
+ {
+ kdDebug(8101) << "Difflines are from a Unified diff..." << endl;
+ return Kompare::Unified;
+ }
+ else if( (*it).find( contextRE, 0 ) == 0 )
+ {
+ kdDebug(8101) << "Difflines are from a Context diff..." << endl;
+ return Kompare::Context;
+ }
+ else if( (*it).find( normalRE, 0 ) == 0 )
+ {
+ kdDebug(8101) << "Difflines are from a Normal diff..." << endl;
+ return Kompare::Normal;
+ }
+ else if( (*it).find( rcsRE, 0 ) == 0 )
+ {
+ kdDebug(8101) << "Difflines are from a RCS diff..." << endl;
+ return Kompare::RCS;
+ }
+ ++it;
+ }
+ kdDebug(8101) << "Difflines are from an unknown diff..." << endl;
+ return Kompare::UnknownFormat;
+}
+
+bool PerforceParser::parseContextDiffHeader()
+{
+// kdDebug(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)++ ) )
+ {
+// kdDebug(8101) << "Matched length Header1 = " << m_contextDiffHeader1.matchedLength() << endl;
+// kdDebug(8101) << "Matched string Header1 = " << m_contextDiffHeader1.cap( 0 ) << endl;
+// kdDebug(8101) << "First capture Header1 = " << m_contextDiffHeader1.cap( 1 ) << endl;
+// kdDebug(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 ) );
+ kdDebug(8101) << "Matched length = " << sourceFileRE.matchedLength() << endl;
+ kdDebug(8101) << "Matched length = " << destinationFileRE.matchedLength() << endl;
+ kdDebug(8101) << "Captured texts = " << sourceFileRE.capturedTexts() << endl;
+ kdDebug(8101) << "Captured texts = " << destinationFileRE.capturedTexts() << endl;
+ kdDebug(8101) << "Source File : " << sourceFileRE.cap( 1 ) << endl;
+ kdDebug(8101) << "Destination File : " << destinationFileRE.cap( 1 ) << endl;
+ m_currentModel->setSourceFile ( sourceFileRE.cap( 1 ) );
+ m_currentModel->setDestinationFile( destinationFileRE.cap( 1 ) );
+
+ result = true;
+
+ break;
+ }
+ else
+ {
+ kdDebug(8101) << "Matched length = " << m_contextDiffHeader1.matchedLength() << endl;
+ kdDebug(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 )
+ {
+ kdDebug(8101) << "Line = " << *m_diffIterator << endl;
+ kdDebug(8101) << "String length = " << (*m_diffIterator).length() << endl;
+ if ( m_normalDiffHeader.exactMatch( *(m_diffIterator)++ ) )
+ {
+ kdDebug(8101) << "Matched length Header1 = " << m_normalDiffHeader.matchedLength() << endl;
+ kdDebug(8101) << "Matched string Header1 = " << m_normalDiffHeader.cap( 0 ) << endl;
+ kdDebug(8101) << "First capture Header1 = \"" << m_normalDiffHeader.cap( 1 ) << "\"" << endl;
+ kdDebug(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 ) );
+ kdDebug(8101) << "Matched length = " << sourceFileRE.matchedLength() << endl;
+ kdDebug(8101) << "Matched length = " << destinationFileRE.matchedLength() << endl;
+ kdDebug(8101) << "Captured texts = " << sourceFileRE.capturedTexts() << endl;
+ kdDebug(8101) << "Captured texts = " << destinationFileRE.capturedTexts() << endl;
+ kdDebug(8101) << "Source File : " << sourceFileRE.cap( 1 ) << endl;
+ kdDebug(8101) << "Destination File : " << destinationFileRE.cap( 1 ) << endl;
+ m_currentModel->setSourceFile ( sourceFileRE.cap( 1 ) );
+ m_currentModel->setDestinationFile( destinationFileRE.cap( 1 ) );
+
+ result = true;
+
+ break;
+ }
+ else
+ {
+ kdDebug(8101) << "Matched length = " << m_normalDiffHeader.matchedLength() << endl;
+ kdDebug(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 )
+ {
+// kdDebug(8101) << "Line = " << *m_diffIterator << endl;
+// kdDebug(8101) << "String length = " << (*m_diffIterator).length() << endl;
+ if ( m_unifiedDiffHeader1.exactMatch( *(m_diffIterator)++ ) )
+ {
+// kdDebug(8101) << "Matched length Header1 = " << m_unifiedDiffHeader1.matchedLength() << endl;
+// kdDebug(8101) << "Matched string Header1 = " << m_unifiedDiffHeader1.cap( 0 ) << endl;
+// kdDebug(8101) << "First capture Header1 = \"" << m_unifiedDiffHeader1.cap( 1 ) << "\"" << endl;
+// kdDebug(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 ) );
+// kdDebug(8101) << "Matched length = " << sourceFileRE.matchedLength() << endl;
+// kdDebug(8101) << "Matched length = " << destinationFileRE.matchedLength() << endl;
+// kdDebug(8101) << "Captured texts = " << sourceFileRE.capturedTexts() << endl;
+// kdDebug(8101) << "Captured texts = " << destinationFileRE.capturedTexts() << endl;
+// kdDebug(8101) << "Source File : " << sourceFileRE.cap( 1 ) << endl;
+// kdDebug(8101) << "Destination File : " << destinationFileRE.cap( 1 ) << endl;
+ m_currentModel->setSourceFile ( sourceFileRE.cap( 1 ) );
+ m_currentModel->setDestinationFile( destinationFileRE.cap( 1 ) );
+
+ result = true;
+
+ break;
+ }
+ else
+ {
+// kdDebug(8101) << "Matched length = " << m_unifiedDiffHeader1.matchedLength() << endl;
+// kdDebug(8101) << "Captured texts = " << m_unifiedDiffHeader1.capturedTexts() << endl;
+ }
+ }
+
+ return result;
+}
+
diff --git a/kompare/libdiff2/perforceparser.h b/kompare/libdiff2/perforceparser.h
new file mode 100644
index 00000000..99973167
--- /dev/null
+++ b/kompare/libdiff2/perforceparser.h
@@ -0,0 +1,44 @@
+/**************************************************************************
+** perforceparser.h
+** -------------------
+** begin : Sun Sep 8 20:58:59 2002
+** copyright : (c) 2002-2004 Otto Bruggeman
+** email : otto.bruggeman@home.nl
+**
+***************************************************************************/
+/***************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the 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/kompare/main.cpp b/kompare/main.cpp
new file mode 100644
index 00000000..00827c4d
--- /dev/null
+++ b/kompare/main.cpp
@@ -0,0 +1,217 @@
+/***************************************************************************
+ main.cpp - description
+ -------------------
+ begin : Sun Mar 4 2001
+ copyright : (C) 2001-2004 Otto Bruggeman
+ (C) 2001-2003 John Firebaugh
+ email : otto.bruggeman@home.nl
+ jfirebaugh@kde.org
+****************************************************************************/
+
+/***************************************************************************
+**
+** This program is free software; you can redistribute it and/or modify
+** it under the terms of the GNU General Public License as published by
+** the Free Software Foundation; either version 2 of the License, or
+** (at your option) any later version.
+**
+***************************************************************************/
+
+
+#include <kaboutdata.h>
+#include <kcmdlineargs.h>
+#include <kdebug.h>
+#include <kfile.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+
+#include "kompare_part.h"
+#include "kompare_shell.h"
+#include "kompareurldialog.h"
+
+static const char description[] =
+ I18N_NOOP("A program to view the differences between files and optionally generate a diff" );
+
+static const char version[] = "3.4";
+
+static KCmdLineOptions options[] =
+{
+ { "c", I18N_NOOP( "This will compare URL1 with URL2" ), 0 },
+ { "o", I18N_NOOP( "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." ), 0 },
+ { "b", I18N_NOOP( "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. " ), 0 },
+ { "n", I18N_NOOP( "Disables the check for automatically finding the original file(s) when using '-' as URL with the -o option." ), 0 },
+ { "e <encoding>", I18N_NOOP( "Use this to specify the encoding when calling it from the command line. It will default to the local encoding if not specified." ), 0 },
+ { "+[URL1 [URL2]]",0 , 0 },
+ { "+-", 0, 0 },
+// { "", I18N_NOOP( "" ), 0 },
+ KCmdLineLastOption
+};
+
+int main(int argc, char *argv[])
+{
+ KAboutData aboutData( "kompare", I18N_NOOP("Kompare"), version, description,
+ KAboutData::License_GPL,
+ I18N_NOOP("(c) 2001-2004, John Firebaugh and Otto Bruggeman"), 0, "http://bruggie.dnsalias.org/kompare/" );
+ aboutData.addAuthor( "John Firebaugh", I18N_NOOP("Author"), "jfirebaugh@kde.org" );
+ aboutData.addAuthor( "Otto Bruggeman", I18N_NOOP("Author"), "otto.bruggeman@home.nl" );
+ aboutData.addCredit( "Chris Luetchford", I18N_NOOP("Kompare icon artist"), "chris@os11.com" );
+ aboutData.addCredit( "Malte Starostik", I18N_NOOP("A lot of good advice"), "malte@kde.org" );
+ aboutData.addCredit( "Bernd Gehrmann", I18N_NOOP("Cervisia diff viewer"), "bernd@physik.hu-berlin.de" );
+
+ KCmdLineArgs::init(argc, argv, &aboutData);
+ KCmdLineArgs::addCmdLineOptions( options );
+ KApplication app;
+ bool difault = false;
+
+ // see if we are starting with session management
+ if (app.isRestored())
+ {
+ RESTORE(KompareShell)
+ }
+ else
+ {
+ KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
+
+ KompareShell* ks;
+
+ kdDebug( 8100 ) << "Arg Count = " << args->count() << endl;
+ for ( int i=0; i < args->count(); i++ )
+ {
+ kdDebug( 8100 ) << "Argument " << (i+1) << ": " << args->arg( i ) << endl;
+ }
+
+ if ( args->isSet( "e" ) )
+ {
+ // Encoding given...
+ // FIXME: Need to implement this...
+ }
+
+ if ( args->isSet( "o" ) )
+ {
+ kdDebug( 8100 ) << "Option -o is set" << endl;
+ if ( args->count() != 1 )
+ {
+ difault = true;
+ }
+ else
+ {
+ ks = new KompareShell();
+ ks->show();
+ kdDebug( 8100 ) << "OpenDiff..." << endl;
+ if ( ( strlen( args->arg(0) ) == 1 ) && ( *(args->arg(0)) == '-' ) )
+ ks->openStdin();
+ else
+ ks->openDiff( args->url( 0 ) );
+ difault = false;
+ }
+ }
+ else if ( args->isSet( "c" ) )
+ {
+ kdDebug( 8100 ) << "Option -c is set" << endl;
+ if ( args->count() != 2 )
+ {
+ KCmdLineArgs::usage( "kompare" );
+ difault = true;
+ }
+ else
+ {
+ ks = new KompareShell();
+ ks->show();
+ KURL url0 = args->url( 0 );
+ kdDebug( 8100 ) << "URL0 = " << url0.url() << endl;
+ KURL url1 = args->url( 1 );
+ kdDebug( 8100 ) << "URL1 = " << url1.url() << endl;
+ ks->compare( url0, url1 );
+ difault = false;
+ }
+ }
+ else if ( args->isSet( "b" ) )
+ {
+ kdDebug( 8100 ) << "Option -b is set" << endl;
+ if ( args->count() != 2 )
+ {
+ KCmdLineArgs::usage( "kompare" );
+ difault = true;
+ }
+ else
+ {
+ ks = new KompareShell();
+ ks->show();
+ kdDebug( 8100 ) << "blend..." << endl;
+ KURL url0 = args->url( 0 );
+ kdDebug( 8100 ) << "URL0 = " << url0.url() << endl;
+ KURL url1 = args->url( 1 );
+ kdDebug( 8100 ) << "URL1 = " << url1.url() << endl;
+ ks->blend( url0, url1 );
+ difault = false;
+ }
+ }
+ else if ( args->count() == 1 )
+ {
+ ks = new KompareShell();
+ ks->show();
+
+ kdDebug( 8100 ) << "Single file. so openDiff/openStdin is only possible..." << endl;
+ if ( ( strlen( args->arg(0) ) == 1 && *(args->arg(0)) == '-' ) )
+ 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 = new KompareShell();
+ ks->show();
+ kdDebug( 8100 ) << "Dunno, we'll have to figure it out later, trying compare for now..." << endl;
+ KURL url0 = args->url( 0 );
+ kdDebug( 8100 ) << "URL0 = " << url0.url() << endl;
+ KURL url1 = args->url( 1 );
+ kdDebug( 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 = new KompareURLDialog();
+
+ dialog->setCaption( i18n("Compare Files or Folders") );
+ dialog->setFirstGroupBoxTitle( i18n( "Source" ) );
+ dialog->setSecondGroupBoxTitle( i18n( "Destination" ) );
+
+ KGuiItem compareGuiItem( i18n( "Compare" ), QString::null, 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->setButtonOK( 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 = new KompareShell();
+ ks->show();
+ ks->viewPart()->setEncoding( dialog->encoding() );
+ ks->compare( dialog->getFirstURL(), dialog->getSecondURL() );
+ }
+ else
+ return -1;
+
+ delete dialog;
+ }
+
+ args->clear();
+ }
+
+ return kapp->exec();
+}
+
+/* vim: set ts=4 sw=4 noet: */
+
diff --git a/kompare/pics/Makefile.am b/kompare/pics/Makefile.am
new file mode 100644
index 00000000..f8961b29
--- /dev/null
+++ b/kompare/pics/Makefile.am
@@ -0,0 +1,3 @@
+KDE_ICON = AUTO
+
+
diff --git a/kompare/pics/hi128-app-kompare.png b/kompare/pics/hi128-app-kompare.png
new file mode 100644
index 00000000..27d8e576
--- /dev/null
+++ b/kompare/pics/hi128-app-kompare.png
Binary files differ
diff --git a/kompare/pics/hi16-app-kompare.png b/kompare/pics/hi16-app-kompare.png
new file mode 100644
index 00000000..6dbccf11
--- /dev/null
+++ b/kompare/pics/hi16-app-kompare.png
Binary files differ
diff --git a/kompare/pics/hi22-app-kompare.png b/kompare/pics/hi22-app-kompare.png
new file mode 100644
index 00000000..55c53514
--- /dev/null
+++ b/kompare/pics/hi22-app-kompare.png
Binary files differ
diff --git a/kompare/pics/hi32-app-kompare.png b/kompare/pics/hi32-app-kompare.png
new file mode 100644
index 00000000..68011ccf
--- /dev/null
+++ b/kompare/pics/hi32-app-kompare.png
Binary files differ
diff --git a/kompare/pics/hi48-app-kompare.png b/kompare/pics/hi48-app-kompare.png
new file mode 100644
index 00000000..fb04abca
--- /dev/null
+++ b/kompare/pics/hi48-app-kompare.png
Binary files differ
diff --git a/kompare/pics/hisc-app-kompare.svgz b/kompare/pics/hisc-app-kompare.svgz
new file mode 100644
index 00000000..35b0fb6c
--- /dev/null
+++ b/kompare/pics/hisc-app-kompare.svgz
Binary files 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 <qvariant.h>
+ #include <qcolor.h>
+! #include "../kdatastream.h"
+ #include "../dcopclient.h"
+ #include "../dcopref.h"
+! #include <stdlib.h>
+! #include <stdio.h>
+! #include <ctype.h>
+
+ #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 <ctype.h>
+! #include <stdio.h>
+! #include <stdlib.h>
+!
+ #include <qcolor.h>
+! #include <qdir.h>
+! #include <qfile.h>
+! #include <qfileinfo.h>
+! #include <qmap.h>
+! #include <qstringlist.h>
+! #include <qtextstream.h>
+! #include <qvariant.h>
+!
+! // putenv() is not available on all platforms, so make sure the emulation
+! // wrapper is available in those cases by loading config.h!
+! #include <config.h>
+!
+ #include "../dcopclient.h"
+ #include "../dcopref.h"
+! #include "../kdatastream.h"
+
+ #include "marshall.cpp"
+
++ typedef QMap<QString, QString> 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 <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<QString>";
+! if (type == "QCStringList")
+! type = "QValueList<QCString>";
+! 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<QString>";
+! if (type == "QCStringList")
+! type = "QValueList<QCString>";
+! 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 <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<QString, QString> UserList;
+
+.
+28,30c
+#include "../kdatastream.h"
+.
+25c
+#include <qdir.h>
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qmap.h>
+#include <qstringlist.h>
+#include <qtextstream.h>
+#include <qvariant.h>
+
+// putenv() is not available on all platforms, so make sure the emulation
+// wrapper is available in those cases by loading config.h!
+#include <config.h>
+
+.
+23c
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+.
+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<QString>";
+ if (type == "QCStringList")
+ type = "QValueList<QCString>";
+ 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 <qvariant.h>
+---
+> #include <ctype.h>
+> #include <stdio.h>
+> #include <stdlib.h>
+>
+25c28,39
+< #include "../kdatastream.h"
+---
+> #include <qdir.h>
+> #include <qfile.h>
+> #include <qfileinfo.h>
+> #include <qmap.h>
+> #include <qstringlist.h>
+> #include <qtextstream.h>
+> #include <qvariant.h>
+>
+> // putenv() is not available on all platforms, so make sure the emulation
+> // wrapper is available in those cases by loading config.h!
+> #include <config.h>
+>
+28,30c42
+< #include <stdlib.h>
+< #include <stdio.h>
+< #include <ctype.h>
+---
+> #include "../kdatastream.h"
+33a46,47
+> typedef QMap<QString, QString> 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 <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<QString>";
+< if (type == "QCStringList")
+< type = "QValueList<QCString>";
+< 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<QString>";
+> if (type == "QCStringList")
+> type = "QValueList<QCString>";
+> 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 <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+d25 1
+a25 12
+#include <qdir.h>
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qmap.h>
+#include <qstringlist.h>
+#include <qtextstream.h>
+#include <qvariant.h>
+
+// putenv() is not available on all platforms, so make sure the emulation
+// wrapper is available in those cases by loading config.h!
+#include <config.h>
+
+d28 3
+a30 1
+#include "../kdatastream.h"
+a33 2
+typedef QMap<QString, QString> 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 <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<QString>";
+ if (type == "QCStringList")
+ type = "QValueList<QCString>";
+ 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 <qvariant.h>
++#include <ctype.h>
++#include <stdio.h>
++#include <stdlib.h>
++
+ #include <qcolor.h>
+-#include "../kdatastream.h"
++#include <qdir.h>
++#include <qfile.h>
++#include <qfileinfo.h>
++#include <qmap.h>
++#include <qstringlist.h>
++#include <qtextstream.h>
++#include <qvariant.h>
++
++// putenv() is not available on all platforms, so make sure the emulation
++// wrapper is available in those cases by loading config.h!
++#include <config.h>
++
+ #include "../dcopclient.h"
+ #include "../dcopref.h"
+-#include <stdlib.h>
+-#include <stdio.h>
+-#include <ctype.h>
++#include "../kdatastream.h"
+
+ #include "marshall.cpp"
+
++typedef QMap<QString, QString> 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 <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<QString>";
+- if (type == "QCStringList")
+- type = "QValueList<QCString>";
+- 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<QString>";
++ if (type == "QCStringList")
++ type = "QValueList<QCString>";
++ 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 <qvariant.h>
+ #include <qcolor.h>
+! #include "../kdatastream.h"
+ #include "../dcopclient.h"
+ #include "../dcopref.h"
+! #include <stdlib.h>
+! #include <stdio.h>
+! #include <ctype.h>
+
+ #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 <ctype.h>
+! #include <stdio.h>
+! #include <stdlib.h>
+!
+ #include <qcolor.h>
+! #include <qdir.h>
+! #include <qfile.h>
+! #include <qfileinfo.h>
+! #include <qmap.h>
+! #include <qstringlist.h>
+! #include <qtextstream.h>
+! #include <qvariant.h>
+!
+! // putenv() is not available on all platforms, so make sure the emulation
+! // wrapper is available in those cases by loading config.h!
+! #include <config.h>
+!
+ #include "../dcopclient.h"
+ #include "../dcopref.h"
+! #include "../kdatastream.h"
+
+ #include "marshall.cpp"
+
++ typedef QMap<QString, QString> 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 <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<QString>";
+! if (type == "QCStringList")
+! type = "QValueList<QCString>";
+! 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<QString>";
+! if (type == "QCStringList")
+! type = "QValueList<QCString>";
+! 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 <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<QString, QString> UserList;
+
+.
+28,30c
+#include "../kdatastream.h"
+.
+25c
+#include <qdir.h>
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qmap.h>
+#include <qstringlist.h>
+#include <qtextstream.h>
+#include <qvariant.h>
+
+// putenv() is not available on all platforms, so make sure the emulation
+// wrapper is available in those cases by loading config.h!
+#include <config.h>
+
+.
+23c
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+.
+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<QString>";
+ if (type == "QCStringList")
+ type = "QValueList<QCString>";
+ 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 <qvariant.h>
+---
+> #include <ctype.h>
+> #include <stdio.h>
+> #include <stdlib.h>
+>
+25c28,39
+< #include "../kdatastream.h"
+---
+> #include <qdir.h>
+> #include <qfile.h>
+> #include <qfileinfo.h>
+> #include <qmap.h>
+> #include <qstringlist.h>
+> #include <qtextstream.h>
+> #include <qvariant.h>
+>
+> // putenv() is not available on all platforms, so make sure the emulation
+> // wrapper is available in those cases by loading config.h!
+> #include <config.h>
+>
+28,30c42
+< #include <stdlib.h>
+< #include <stdio.h>
+< #include <ctype.h>
+---
+> #include "../kdatastream.h"
+33a46,47
+> typedef QMap<QString, QString> 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 <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<QString>";
+< if (type == "QCStringList")
+< type = "QValueList<QCString>";
+< 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<QString>";
+> if (type == "QCStringList")
+> type = "QValueList<QCString>";
+> 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 <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+d25 1
+a25 12
+#include <qdir.h>
+#include <qfile.h>
+#include <qfileinfo.h>
+#include <qmap.h>
+#include <qstringlist.h>
+#include <qtextstream.h>
+#include <qvariant.h>
+
+// putenv() is not available on all platforms, so make sure the emulation
+// wrapper is available in those cases by loading config.h!
+#include <config.h>
+
+d28 3
+a30 1
+#include "../kdatastream.h"
+a33 2
+typedef QMap<QString, QString> 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 <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<QString>";
+ if (type == "QCStringList")
+ type = "QValueList<QCString>";
+ 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 <qvariant.h>
++#include <ctype.h>
++#include <stdio.h>
++#include <stdlib.h>
++
+ #include <qcolor.h>
+-#include "../kdatastream.h"
++#include <qdir.h>
++#include <qfile.h>
++#include <qfileinfo.h>
++#include <qmap.h>
++#include <qstringlist.h>
++#include <qtextstream.h>
++#include <qvariant.h>
++
++// putenv() is not available on all platforms, so make sure the emulation
++// wrapper is available in those cases by loading config.h!
++#include <config.h>
++
+ #include "../dcopclient.h"
+ #include "../dcopref.h"
+-#include <stdlib.h>
+-#include <stdio.h>
+-#include <ctype.h>
++#include "../kdatastream.h"
+
+ #include "marshall.cpp"
+
++typedef QMap<QString, QString> 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 <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<QString>";
+- if (type == "QCStringList")
+- type = "QValueList<QCString>";
+- 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<QString>";
++ if (type == "QCStringList")
++ type = "QValueList<QCString>";
++ 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 <pfremy@kde.com>
+!
+!
+! 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 <pfremy@kde.com>
+<
+<
+< 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 <pfremy@kde.com>
+-
+-
+-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)
+