diff --git a/ChangeLog b/ChangeLog index 083bf1a..d67e688 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,573 +1,574 @@ Version 1.7.0 - 2017-01-23 ============================ -ported to QT5/KDEFramework5 -depreciated cmake 2.8.12 support in master -abandonded Konqueror specific plugin no longer applicable. -kde4 support dropped due to extensive changes needed for QT5/KDEFramework5 -QT5 now required because of above changes. -bugfix: Show Indentical files setting not applied after rescan. -Change parameter errors to use dialog on Linux as well as console output. -Don't sort twice when sorting in reverse order. -fix memmory leak introduced in 0.9.91. -change version numbering. +-scrap ClearCase support Version 0.9.98 - 2014-07-04 =========================== - Text rendering now with QTextLayout fixes the following issues - Correct handling for variable width fonts. - Corrected display of highlighted text with Qt4.8.x on Ubuntu and Mac. - Improved handling of texts with both right to left and left to right languages (mixed Arabic and western texts). - Improved handling of Chinese and Japanese. - Whitespace characters are now shown as dots for spaces and arrows for tabs, and not only in differences. - Fixed symlink comparison (Qt4 symLinkTarget returns absolute paths) - Text analysis for rendering with QTextLayout is interruptable and multithreaded. (See progressbar and abort-button in statusbar) - Fix for saving to relative path in KDE-environments. (Patch from Harald Sitter) - Fixed bug in 0.9.97: Directory compare was always case sensitive. - Fix for saving files on KDE with relative path specified via command line option -o. - Fixed problem with KIO (nonlocal urls). - Improved Mac support. - Write --confighelp information to stdout instead of stderr. - Directory Merge Window: Enabled state of "Delete A And B" now also depends on existence of source file A. - Works now with Qt4 and Qt5 - Progress dialog during printing. - Workaround for bug in QSplitter::childEvent that broke QFileDialog::getSaveFileName Version 0.9.97 - 2012-08-10 =========================== - Memory usage optimized for comparison of large directories. (ca. 1/5 needed) - On Windows use config file .kdiff3rc next to kdiff3.exe if exists. - In overwiev for two way diff show if only one side contains text. - Fix for Fedora by Neal Becker in src-QT4/kdiff3part.desktop. - When word wrap is active toggling line numbers now recalculates the word wrap. - Removed confusing "For compatibility ..." hint from option -qall - Fixed mouse wheel problem. (Patch by David Hay) - Change an encoding in diff text window via click on encoding label. (Patch by Alexey Kostromin) - Fix for tab-key moving focus instead of adding a tab character in MergeResultWindow. - Workaround for git on Cygwin that allows KDiff3 to find files in a Cygwin "/tmp" directory when environment variable "CYGWIN_BIN" is set. (Patch by Nigel Stewart) - Removed iostream dependency (Patch by Nigel Stewart) - Regression test framework (by Maurice van der Pot) - Documentation patch (by Burkard Lueck) - Select text in Find dialog (by Eike Sauer) - If text is selected in either input or output window use that in Find dialog. - Added default directory anti patterns ".hg" and ".git" and file anti patterns ".rej" and ".bak" for better mercurial and git integration. - Command line option --cs doesn't change the config value permanently anymore. - On KDE: Not creating a KDiff3Part anymore. - Windows (Vista or Win 7): Shell context menu in directory comparison view now again displays text. - Windows 64 bit: Diff-Ext-for-KDiff3 context menu now installed as 32 bit and 64 bit versions to allow other 32 bit programs to use it too. - Windows 64 bit specific installer. Version 0.9.96 - 2011-09-02 =========================== - KDiff3FileItemActionPlugin : Context menu that also works in dolphin (for KDE>=4.6) - Parser for preprocessor commands. (Allows single apostrophs ') - On Windows if the preprocessor command is "sed" try to use a "bin\sed.exe" next to the kdiff3.exe, if available. - Warn if conversion errors appear (Invalid characters) - Fix crash on A/B-overview (infinite recursion) - Fix clearcase temp files not deleted problem on windows - KDiff3 plugin: When launching KDiff3 konqueror isn't blocked anymore - String corrections (Frederik Schwarzer) - Fixed writing to KIO. - OS2-Port (Patch by Silvan Scherrer) - Fixed problem where destination directory would be renamed or deleted during copy operation. Now if the destination directory exists only the files inside will be copied. - In merge: Separate lines where the automatic choice would be the same but for different reasons. - Fixed some problems with huge files in directory comparison mode (>2GB) (but direct comparison is still not possible) - Fixed documentation compile errors with KDE>=4.5 - Fixed white space merge default options - Fixed regexp test tool. - Exclude printing code if Qt was compiled without printing support. - For Windows: Fixed handling of unicode characters in command line parameters. - Improved "old mac" lineendstyle handling: Break lines. - Detect encoding specified in xml header or html "meta" tag. Version 0.9.95 - 2009/03/03 =========================== - Show line end style for each file. - Updated message translations. - Fixed permissions when writing executable file. (Un*x only) - Fixed IgnorableCmdLineOptions (important for SVNs '-u'-option) - Directory merge: Error when either B or C is changed and the other is deleted. (User choice required.) - Qt-only Un*x-version looks for translations in /usr/share/locale//LC_MESSAGES/kdiff3.qm (for Debian, by Eike Sauer) - New script: po/create_qm_files: To create and install translations for Qt-only version. Version 0.9.94 - 2009/01/17 =========================== - Fix for hidden text windows with --auto-flag. - Fix for pasting clipboard truncated text if it contained characters that needed more than one byte in UTF-8 encoding. - Fix for horizontal scrolling if word wrap is enabled. - Directory tree: files hidden due to options (e.g. patterns etc.) don't affect folder equality any more. - KDE: KIO-progress dialog is now hidden. (KDiff3 has its own progress dialog.) - Directory merge: Default op for change in either B or C and delete in the other is now merge (previously copy). - Directory merge: Not preserving merge operation after reload, because it might have changed. Version 0.9.93 - 2009/01/06 =========================== - Support for KDE4 (with much porting help from Valentin Rusu) - Fix for diff_ext_for_kdiff3 (by Sergey Zorin) - Win32-Installation: SendTo-integration fixed for Vista. - Optional auto detection of line end style for saving. - Option to close on ESC (default is off) - Option to align B and C for 3 input files (default is off which is usually better for merging). Version 0.9.92 - 2007/04/15 =========================== - Windows installer now allows you to install KDiff3 as Clearcase Diff and Merge Tool - Windows installer "SVN Merge tool" corrected: Not creating $AppData\Subversion\config subdir anymore. - KDE-Konqueror plugin: Launch KDiff3 from Konqueror. (Similar to Diff-Ext on Windows.) - Qt4-version - Printing crash fixed - Compilation issue for Mac fixed - Dir Rescan keeps settings for Show identical files etc. - Bugfix: Empty file and not existing file were detected as binary equal. - Temp file names use the process id in file name to allow several instances. - Suppress flicker during startup. (Don't show status info window on creation.) - New File comparison mode: Trust the size and date, but use binary comparison if date doesn't match (unsafe) - After explicitly selecting files any file of the selected may be right clicked for context menu. - Open dialog also shows current directories in directory comparison mode. - Writing a file via --auto option didn't work for relative files. (Reported by Guilhem Bichot) - New option for history merge: Max number of history entries - New option "Auto save and quit on merge without conflicts" - Directory Merge with Case sensitivity disabled: Correct destination filename chosen for merge. Version 0.9.91 - 2006/10/29 =========================== - Encoding auto detection - Fix for crash after double click below last line - Saving of maximized window-state (Patch by Robert Vock) - Separated Merge-options in own tab because "Diff and Merge"-options tab got too big. - When pasting multiple lines into openfile dialog only first line is kept - Drawing in directory view fixed. - When specifying a separate output dir then for equal files a copy operation will also be offered. - Windows specific: - Windows installer problems fixed for users without admin-rights - Fix for slow startup problem on Windows (Patch by Manfred Koehler) - New: diff-ext-for-kdiff3 - Shell extension (originally by Sergey Zorin) - Qt4-version: - Saving of merge-result didn't work. - Start external processes directly without cmd.exe-window - Rewrote everything requiring Qt3-support Version 0.9.90 - 2006/05/14 =========================== - Fixed KIO-problems of type "File exists" with tempfiles (introduced in 0.9.89) - Fix for manual alignment with 3 files which caused crash (new feature in 0.9.89) - Fix for Alt-Left caused crash for leftmost window on windows (due to changed compiler) - Use of WResizeNoErase|WRepaintNoErase instead of WNoAutoErase (fix for compiler error with Qt3.1) - Removed #include which is (currently) unneeded and required extra dependencies. - Removed "Save/Load Directory Merge State ..." in directory menu. (These aren't working yet.) - Fixed crash when used as Diff-part with KDevelop. - Preserve executable bit when overwriting an existing file. Version 0.9.89 - 2006/04/09 =========================== New features: - Version control history auto merge plus sorting - Auto merge regular expression - Splitting and joining differences for merging - Manual Diff Alignment tool - Printing of differences - Colorsettings for Dir-Colors - Dir-show identical/different/A-only/B-only/C-only files with immediate effect (instead of option "List only deltas") - Filename-edit above DiffInputWindows - Windows-Context Menu in A/B/C-columns for dir-comparison (Windows only) - Edit Menu: Select All (Ctrl-A) - New commandline options: --config filename: Select an individual config file. (Now also available for Windows and Qt-only version.) --cs config: Change one specific setting via the command line. (For settings that were previously adjustable via GUI only.) --confighelp: Show available config items and their current values. - Dircomp: "Compare/Merge explicitly selected files" (Select files/dirs by clicking icons in columns A/B/C) - User definable ignored command line options. - Ability to swap pathnames in open dialog - "Ignore"-button in error dialog when option not understood (Windows only) - Quadratical scroll speedup during selection when mouse moves out of the diff input window. Bugfixes, redesign: - Preparations for Qt4-Port + some redesign - GNU-Diff algorithm improved to be independent of line endings (needed for manual diff alignment) - Avoid restoring a window where it is almost invisible (if moved almost out of the screen area) - Go to next delta honors special "A vs. B", "A vs. C" or "B vs. C" overview when active. (Patch by Vladan Bato) - DirectoryMergeWindow: File/Antifile and DirPattern changes will update immediately without rescan. - Blue toolbar icons (for better visibility of disabled state) - Bugfix: Crash when merging and selecting "Choose A/B/C for all unsolved conflicts" and one of the solved conflicts contained no lines in chosen input. - Fix: With --auto option, GUI stays invisible if not necessary - Fixed odd ProgressDialog-behaviour when continuing after an error or abort. - Directory merge: Fixed FollowFileLinks. (Didn't work when copying a file.) - Initial position now (x=0,y=22). This solves a problem on some Macs. - Better alignment of B and C in 3-file comparison - Correctly updating the selection when scrolling via keys and mouse is pressed - Horizontal scrolling in right-to-left language caused vertical lines - fixed. Version 0.9.88 - 2005/25/02 =========================== - Fixed crash that occurred in Directory Comparison mode "Full-Analysis". - Fix for Windows: Didn't save encoding correctly. - Many translations updated. Version 0.9.87 - 2005/30/01 =========================== - Unicode16 and UTF8 support (Internal data format is now QString (Unicode16). Conversion during save and load.) - Directory "Full Analysis": Equality-Coloring for files with only whitespace differences. (Michael Denio) - Support for right to left languages. - In MergeResultWindow show "" for whitespace-only conflicts - Statusbar shows the number of remaining conflicts and whitespace conflicts. - Go Next/Prev Difference/Conflict now have improved tooltips informing about "Show White Space"-disabled-behaviour. Version 0.9.86 - 2004/06/14 =========================== - Double click on any file in directory merge would close the directory merge window. (Regression in 0.9.85) Version 0.9.85 - 2004/06/14 =========================== - When solving a conflict KDiff3 reports the number of remaining unsolved conflicts in the status bar. Bugfixes: - Fix for MergeResultWindow-contextmenu: All items were disabled always. (new in 0.9.84) - Fix for problem when opening files specified relative to current directory. (new in 0.9.84, qt-only-version) - Fix for compilation with older gcc (2.9x) - Several Word-wrap problems fixed: - Find string with word wrap active didn't work if found text was not in first wrap-line. - overview-position was not updated when toggling word wrap - horizontal scrollbar was not updated when toggling word wrap - current selection was lost when toggling word wrap - selecting a conflict in the diff-text-window didn't work right with word wrap. - Qt-only: Bold attribute for fonts was not persistent - Qt-only: Toolbar position was not persistent - Qt-only: Language-choice shows also the full language name. - Cursor and windows-boundary-lines were always black instead of having the foreground color - Starting KDiff3 with two not existing files showed a dialog saying that files are binary equal. - Errors while starting a directory comparison now also reopens the open-dialog. - Speedup during directory comparison by avoiding unnecessary redraws. (These always creep in again :-() - On KDE: When resetting to default options (or first start) now the default KDE-fixed font will be used. - Mergeresultwindow: Improved behaviour after automatic merge operation. Version 0.9.84 - 2004/05/29 ============================ New Features: - Word Wrap for DiffTextWindow - Directory-Comparison: Option "Full Analysis" allows you to show the number of solved vs. unsolved conflicts or deltas vs. whitespace-changes in the directory tree. - Diff-Menu for Diff-view specific entries - Docs now contain a new chapter for uses of preprocessor and line-matching-preprocessor. - Added several credits which now are also visible in the Qt-only version. - The Qt-only version now also shows all command-line options. Under windows a dialog shows them. - Command line options -u and -L for Subversion-support. - Command line options --L1/2/3 for specifying alias names. - In the Qt-only-version the user-interface-language can be set via the regional-settings (only effective after a restart). - ProgressDialog redesign for recursive use. - Overview now allows you to show the delta between two other files in triplediff-mode. - Option to ignore case which treats case-changes like white space (instead of conversion to upcase). Bugfixes: - Dir-Comp: When one file exists, but the other doesn't then instead the latest used other file was displayed. - Open dialog: When previously a file C was used, but should be empty now, it reappeared unbidden. - Several bugs for 64-bit systems fixed. - Fixed crash when one file ended with a newline and the other did not. - Windows: Case insensitive filename-pattern matching. - Corrected behaviour for files with size 0. - Fix for crash due to a race-condition (Patch by Eike Sauer) - Windows: Scrolling didn't work right when another window was in front. - Mergeresultwindow didn't show correct position when starting a second or later merge. - Fix for problem where sometimes the A/B/C-buttons were in wrong state. - Pasting from selection via the middle mousebutton. Version 0.9.83 - 2004/03/06 =========================== - Reading directorys fixed for Win95/98 - Caseinsensitive filename matching for windows. - Autocopy to selection for systems that support this. (Patch by Stefan Partheymueller) - Drawing during recalc suppressed in merge result editor. - Cursor could go beyond last line in merge result editor. (Corrected NrOfLine-counting.) - Windows: Start with invalid cmd-line-options brings up a messagebox with the list of options. - Corrected encoding when copying to or pasting from clipboard. - Corrected char-by-char-diff at beginning of line. ("012345 12345 xyz" <-> "012345 xyz") - Warning when merging with preprocessor or upcase-conversion enabled. - Rewrite of preprocessing code should fix several problems. E.g.: - Ignore C/C++-comments only worked with a preprocessor active. - Preprocessor output now is input of line-matching preprocessor. - Paste to diff-window, didn't work if LMPP or Ignore C/C++-Comments was set. Version 0.9.82 - 2004/02/02 =========================== - DirectoryMerge: Running merge op for last item in a folder, performed the merge op for all following items in following folders. (Possible data loss!) - Fix: Preprocessors and "Ignore Comments" didn't work at the same time. - Fix: Preprocessors crashed with remote files. - Open-Dialog: When either input is changed, then reset the output to be empty. (To avoid accidental overwrites.) - Icon for "Highlight white space differences." - Editor-Option: Line End Style for saving: Dos/Windows "\r\n" vs. Unix "\n" - Merge output editor: Corrected wrong encoding for output-filename and user-typed characters. - Speedup for reading directories under Windows. - Enhanced progress dialog responsiveness during local file copy. - Fix for non-KDE-version: No URL-encoding when dropping files in open dialog. Version 0.9.81 - 2004/01/08 =========================== - Allow to compile with --enable-final - Bugfix for 3 file-compare (and A or B don't exist, crashed) - Bugfix for crash when second directory is merged - Some keyboard-shortcuts for selection of merge-operation didn't work correctly. - Shortcuts Ctrl-1/2/3 are possible in textmergewindow and in dirmergewindow, depending on the focus. - First steps towards internationalisation - Manpage doc/en/kdiff3.1 by Eike Sauer (for Debian) - Directory rescan shortcut SHIFT-F5 Version 0.9.80 - 2003/12/08 =========================== New Text Diff/Merge Features: - Now using GNU-diff algorithms internally. (Option "External Diff" removed.) - Option for treating C/C++ comments as whitespace during diff. - Bugfix for locale character encoding (+ new option "Use string encoding") - Option for suppressing highlighting in white-space changes. (Also suppresses highlighting in comments and numbers when the respective options are active.) - Merge-menu: Choose A/B/C for all unsolved conflicts. Choose A/B/C for all unsolved whitespace conflicts. - Options to automatically choose a certain source for whitespace conflicts. - Shorcut F5 now used to reload the current file. New Directory-Comparison/Merge Features: - Option to trust filesize. (Some directory services don't copy the date/time correctly.) - Shortcut F7 now starts complete directory merge (previously F5). - Do the selected merge operation for the selected file/dir only "Run Operation For Current Item" (F6). - Shortcuts for selecting the merge operation for the selected item. Ctrl-1/2/3/4/Del select A/B/C/Merge/Delete respectively. Other Improvements: - Several i18n-corrections (by Stephan Binner) - Bugfix for option CVS-ignore: Didn't work correctly in subdirectories. - Bugfix for remote operations: Operation can now be aborted, when KIO-slaves doesn't respond. - Cancel-Button in progress bar. - Default diff-view now again side by side instead of one above the other. Version 0.9.71 - 2003/10/17 =========================== - Windows-Installer by Sebastien Fricker. - Bugfixes for Windows. (Problems with setFont() in paintEvent().) - Default font for Windows now "Courier New" (instead of Courier) - Fix for compilation with gcc 2.95 - Support for Ctrl-Tab under Windows. - Fix for finding documentation. - Fix for problem with directory-sync-mode (new in 0.9.70). - Fix for several subsequent CR-characters in input file. Version 0.9.70 - 2003/09/28 =========================== - Transparent access to URLs via KIO (KDE only): Compare files and directories on ftp, fish, smb, tar etc. ressources. - Workaround for a Win32-bug (Crashed sometimes during selections) - When the merge flag is selected in the open dialog, the directory-tool always starts a merge by default for each file. Without the flag only a diff will be started by default. - Immediately showing progress bar in dir scan. - Showing progress bar for file comparison too. - Directory-menu: Fold/Unfold all subdirs - Bugfix for 3-way auto-merge: A line deleted from the base in B and C resulted in a empty line instead of being completely removed. - Improved locale support - KDiff3 is now a KPart - in KDevelop3 it can be used to compare the current text with the last saved version, or the current version on disk with the last cvs version. - in Konqueror it can be used to look at a unified *.patch-file if one complete version is available too. - Documentation is now in docbook-format. - "Toggle Split Orientation" for Diff-Input windows. (Good for long lines.) - When "Dir and Text Split-Screen-View" is off: Now "Focus Next/Prev Window" also toggles between dir and text-windows. Selecting a file via double click switches to text-screen. - KDiff3 displays a warning when trying to read a dir without the permission. - Directory-Diff-Option "Use CVS-Ignore" to ignore files like CVS does. - Displaying a status message at the end of the directory-comparison. - Cursor in MergeResultWindow is automatically placed at current difference when a jump occurred. (But not when something was selected.) - Fix for cursor blinking in the topline of the MergeResultWindow. Version 0.9.61 - 2003/06/07 =========================== - Compilation problem fixed. - Directory merge: Preserving file attributes and times during copy. (now also for Win32) - Crash fixed, when directory comparison from the command-line was started. Version 0.9.60 - 2003/06/01 =========================== New features: - New ways to select input for the diff window: - Pasting clipboard text into a diff window. - Drag and drop a file from a filemanager (e.g. konqueror) onto a diff window. - Drag and drop text from an editor (e.g. kate) onto a diff window. Reanalysis starts immediately if no merge is in progress. (This should help you to compare similar parts in the same file.) - New/Deleted white lines are now also considered as white deltas. - Configurable keyboard shortcuts for most actions (KDE version only). - The overview now also distinguishes whitespace deltas. New preprocessor options: - You can now define your own external Preprocessor and LineMatchingPreprocessor: - "Convert to upper case", - "Ignore numbers" Fixed bugs: - Directory merge: Preserving file attributes and times during copy. (not for Win32 yet) Source-tree-structure: - Switch to KDevelop3 (Gideon): Renamed subdir "kdiff3" to "src". - xpm-files in xpm-subdirectory. Version 0.9.51 - 2003/04/14 =========================== - Compilation fix for gcc 2.95. Version 0.9.50 - 2003/03/30 =========================== Fixed bugs: - Auto-Advance setting was lost when entering the settings-dialog. - Windows specific: Keys with AltGr-Combination didn't work. - Windows 95/98/ME: Fixed crash when KDiff3 is called used without parameters, and corrected support for external diff. New Features: - Search-function: Search for a string in all open text windows. - Special background colors for current region. - Button to toggle showing of whitespace in differences. - Buttons to go to next/prev unsolved (!) conflict. - While auto-advance waits, no more choices are allowed. - New setting: Auto-advance-delay. (Note that with delay 0 fast clicks might be detected as double clicks and the second click does nothing. My advice: Prefer the keyboard-shortcuts Ctrl-1/2/3) - Functions to Show/Hide Diff Window A, B or C. The other windows then have more space. - Merge editor: The right mouse button selects the current region and lets you choose A, B or C via a popup menu. - Commandline option --auto: No GUI if all conflicts are auto-solvable. - When equal files are compared, then a message box informs you. - Merge current file: When comparing two or three files, the merge can be started with a single click. - Option dialog: Warning for "Defaults" added, because it resets all options. - A warning is given, when the user tries to merge binary files. (i.e. files that contain '\0'-bytes) Changed behaviour: - 3 file automerge: When for a line B==C (and A!=B) then C will be selected. (In older versions this was a conflict. I was convinced that this is no problem.) - Auto-Advance now jumps to next unsolved (!) conflict. - On 256-color-displays KDiff3 uses them. (Previously KDiff3 only used 16 colors.) - On 16-color-displays the Defaults-button in the options dialog selects special colors. Version 0.941 - 2003/02/09 ========================== Fixed bugs: - Qt-only-version: Compile problem corrected. - Documentation: Formatting for tables corrected. Version 0.94 - 2003/02/09 ========================= New features: - Option to use external GNU-diff for line matching. (Sometimes GNU-diff is better, sometimes not: You may choose now.) - In diff-windows a tooltip shows the full path if you move the mouse on the filename. - Speedup of directory-merge operations without user interaction. (Not every item in the tree is made visible anymore. This took too long.) - When opening a file for comparison or merge KDiff3 immediately shows the first difference. - "Go To Top/Bottom"-action have been changed to "Go To First/Last Delta". - Font-Option "Italic For Deltas" added. - Many icons and actions will only be enabled, when the operation is possible. - Icon for merge of current file in directory merge mode added. - New action "Go to Current Delta". - Conflicts where some lines contained only-white-space-changes are now separated from other non-white-space-conflicts. - Experimental: Use as replacement for ClearCase-cleardiffmrg.exe (under Windows only). See main.cpp for details. Fixed bugs: - If files were different, but had the same dates, the "not existant"-icon was shown for one file. Now a error message will be shown if the option "Copy newer instead of merging" is used. - Documentation: Section "The Operation Column" corrected. - Qt-only-version: Fontsize wasn't correctly restored. - Keyboard accelerators didn't work for ToggleActions. Version 0.931 - 2003/01/19 ========================== Fix for compilation problems with gcc version < 3. Version 0.93 - 2003/01/16 ========================= New features: - Directory comparison and merge. (More than 3000 new lines of code only here!!!) - Open-Dialog: Filename specification: If no previous filename is there then start directory is taken from another file. - Message about number of found and automatically solved conflicts. - Support for wheelmouse based scrolling. - New option in Diff-tab: Preserve Carriage Return Characters Fixed bugs: - Save button disabled until all conflicts are solved. - Copy-operation conserves conflict messages "". - Paste operation created pseudo conflicts when the clipboard contained empty lines. - W95/98/ME specific program crash removed. Version 0.92 - 2002/11/04 ========================= Severe bug corrected: - Merge menu: Choose A/B/C Everywhere sometimes lost data. (introduced in 0.9) Version 0.91 - 2002/11/03 ========================= Speed improvements for very big/complicated files: - Faster analysis because of limited search range (can be adjusted). - Faster scrolling and editor behaviour. Fixed bugs: - Compilation problem with gcc 3.2 fixed. - When comparing two lines, matching spaces often were undetected. - Merge editor appended extra empty line when saving. - Sometimes the next diff/conflict wasn't made visible. - The Auto-Advance setting is saved now. - When doing a merge the application now has modified-state, even without further input. (The old method wasn't safe.) - File selection now always in directory of respective file. Version 0.9 - 2002/10/16 ======================== New features: - Qt-only support. Allows compilation under KDE2, Gnome, Mac, Windows, ... Note that KDE3 still gets special treatment. - For Mergers: Auto-Advance after selection, Choose A/B/C everwhere, ... - Commandline: If files with same name in different directories are compared, only the first parameter needs the filename. - Shift-Del, Ctrl-Ins, Shift-Ins supported for Cut/Copy/Paste Fixed bugs: - Make failed on some systems because of missing "minmax.h". - Files where opened for reading, but not closed afterwards. - Vertical scrollbar sometimes didn't work correctly. Version 0.81 - 2002/08/18 ========================= New features: - Now KDE3 is also supported. Previously only KDE2 was supported. - Navigation via click into the overview column now supported. Fixed bugs: - Some input files caused a crash in the diff-algorithm. - The meaning of option "Ignore trivial matches" was inverted. - When selecting a text in one window, this deselects any previously active selection in the same or another window. Version 0.8 - 2002/07/28 ======================== This is the first version to be released. diff --git a/src/ccInstHelper.cpp b/src/ccInstHelper.cpp deleted file mode 100644 index bab0458..0000000 --- a/src/ccInstHelper.cpp +++ /dev/null @@ -1,326 +0,0 @@ -// uninstallHelper.cpp : Defines the entry point for the console application. -// -#include -#include -#include -#include -#include -#include - -//#define __stdcall - -// For compilation download the NSIS source package and modify the following -// line to point to the exdll.h-file -#include "C:/Programme/NSIS/Contrib/ExDll/exdll.h" - -struct ReplacementItem { - const char* fileType; - const char* operationType; -}; - -ReplacementItem g_replacementTable[] = { - {"text_file_delta", "xcompare"}, - {"text_file_delta", "xmerge"}, - {"whole_copy", "xcompare"}, - {"whole_copy", "xmerge"}, - {"z_text_file_delta", "xcompare"}, - {"z_text_file_delta", "xmerge"}, - {"z_whole_copy", "xcompare"}, - {"z_whole_copy", "xmerge"}, - {"_xml", "xcompare"}, - {"_xml", "xmerge"}, - {"_xml2", "xcompare"}, - {"_xml2", "xmerge"}, - {"_rftdef", "xcompare"}, - {"_rftmap", "xcompare"}, - {"_rftvp", "xcompare"}, - {"_xtools", "xcompare"}, - {0, 0}}; - -struct LineItem { - std::string fileType; - std::string opType; - std::string command; - std::string fileOpPart; -}; - -// Return true if successful, else false -bool readAndParseMapFile(const std::string& filename, std::list& lineItemList) -{ - // Read file - FILE* pFile = fopen(filename.c_str(), "r"); - if(pFile) - { - fseek(pFile, 0, SEEK_END); - int size = ftell(pFile); - fseek(pFile, 0, SEEK_SET); - std::vector buf(size); - fread(&buf[0], 1, size, pFile); - fclose(pFile); - - // Replace strings - int lineStartPos = 0; - int wordInLine = 0; - LineItem lineItem; - for(int i = 0; i < size;) - { - if(buf[i] == '\n' || buf[i] == '\r') - { - ++i; - wordInLine = 0; - lineStartPos = i; - continue; - } - if(buf[i] == ' ' || buf[i] == '\t') - { - ++i; - continue; - } - else - { - int wordStartPos = i; - if(wordInLine < 2) - { - while(i < size && !(buf[i] == ' ' || buf[i] == '\t')) - ++i; - - std::string word(&buf[wordStartPos], i - wordStartPos); - if(wordInLine == 0) - lineItem.fileType = word; - else - lineItem.opType = word; - ++wordInLine; - } - else - { - lineItem.fileOpPart = std::string(&buf[lineStartPos], i - lineStartPos); - while(i < size && !(buf[i] == '\n' || buf[i] == '\r')) - ++i; - - std::string word(&buf[wordStartPos], i - wordStartPos); - lineItem.command = word; - lineItemList.push_back(lineItem); - } - } - } - } - else - { - return false; - } - return true; -} - -bool writeMapFile(const std::string& filename, const std::list& lineItemList) -{ - FILE* pFile = fopen(filename.c_str(), "w"); - if(pFile) - { - std::list::const_iterator i = lineItemList.begin(); - for(; i != lineItemList.end(); ++i) - { - const LineItem& li = *i; - fprintf(pFile, "%s%s\n", li.fileOpPart.c_str(), li.command.c_str()); - } - fclose(pFile); - } - else - { - return false; - } - return true; -} - -std::string toUpper(const std::string& s) -{ - std::string s2 = s; - - for(unsigned int i = 0; i < s.length(); ++i) - { - s2[i] = toupper(s2[i]); - } - return s2; -} - -int integrateWithClearCase(const char* subCommand, const char* kdiff3CommandPath) -{ - std::string installCommand = subCommand; // "install" or "uninstall" or "existsClearCase" - std::string kdiff3Command = kdiff3CommandPath; - - /* - std::wstring installCommand = subCommand; // "install" or "uninstall" - std::wstring wKDiff3Command = kdiff3CommandPath; - std::string kdiff3Command; - kdiff3Command.reserve( wKDiff3Command.length()+1 ); - kdiff3Command.resize( wKDiff3Command.length() ); - BOOL bUsedDefaultChar = FALSE; - int successLen = WideCharToMultiByte( CP_ACP, 0, - wKDiff3Command.c_str(), int(wKDiff3Command.length()), - &kdiff3Command[0], int(kdiff3Command.length()), 0, &bUsedDefaultChar ); - - if ( successLen != kdiff3Command.length() || bUsedDefaultChar ) - { - std::cerr << "KDiff3 command contains characters that don't map to ansi code page.\n" - "Aborting clearcase installation.\n" - "Try to install KDiff3 in another path that doesn't require special characters.\n"; - return -1; - } - */ - - // Try to locate cleartool, the clearcase tool in the path - char buffer[1000]; - char* pLastPart = 0; - int len = SearchPathA(0, "cleartool.exe", 0, sizeof(buffer) / sizeof(buffer[0]), - buffer, &pLastPart); - if(len > 0 && len + 1 < int(sizeof(buffer) / sizeof(buffer[0])) && pLastPart) - { - pLastPart[-1] = 0; - pLastPart = strrchr(buffer, '\\'); // cd up (because cleartool.exe is in bin subdir) - if(pLastPart) - pLastPart[1] = 0; - - std::string path(buffer); - path += "lib\\mgrs\\map"; - std::string bakName = path + ".preKDiff3Install"; - - if(installCommand == "existsClearCase") - { - return 1; - } - else if(installCommand == "install") - { - std::list lineItemList; - bool bSuccess = readAndParseMapFile(path, lineItemList); - if(!bSuccess) - { - fprintf(stderr, "Error reading original map file.\n"); - return -1; - } - - // Create backup - if(access(bakName.c_str(), 0) != 0) // Create backup only if not exists yet - { - if(rename(path.c_str(), bakName.c_str())) - { - fprintf(stderr, "Error renaming original map file.\n"); - return -1; - } - } - - std::list::iterator i = lineItemList.begin(); - for(; i != lineItemList.end(); ++i) - { - LineItem& li = *i; - for(int j = 0;; ++j) - { - ReplacementItem& ri = g_replacementTable[j]; - if(ri.fileType == 0 || ri.operationType == 0) - break; - if(li.fileType == ri.fileType && li.opType == ri.operationType) - { - li.command = kdiff3Command.c_str(); - break; - } - } - } - - bSuccess = writeMapFile(path, lineItemList); - if(!bSuccess) - { - if(rename(bakName.c_str(), path.c_str())) - fprintf(stderr, "Error writing new map file, restoring old file also failed.\n"); - else - fprintf(stderr, "Error writing new map file, old file restored.\n"); - - return -1; - } - } - else if(installCommand == "uninstall") - { - std::list lineItemList; - bool bSuccess = readAndParseMapFile(path, lineItemList); - if(!bSuccess) - { - fprintf(stderr, "Error reading original map file\n."); - return -1; - } - - std::list lineItemListBak; - bSuccess = readAndParseMapFile(bakName, lineItemListBak); - if(!bSuccess) - { - fprintf(stderr, "Error reading backup map file.\n"); - return -1; - } - - std::list::iterator i = lineItemList.begin(); - for(; i != lineItemList.end(); ++i) - { - LineItem& li = *i; - if((int)toUpper(li.command).find("KDIFF3") >= 0) - { - std::list::const_iterator j = lineItemListBak.begin(); - for(; j != lineItemListBak.end(); ++j) - { - const LineItem& bi = *j; // backup iterator - if(li.fileType == bi.fileType && li.opType == bi.opType) - { - li.command = bi.command; - break; - } - } - } - } - - bSuccess = writeMapFile(path, lineItemList); - if(!bSuccess) - { - fprintf(stderr, "Error writing map file."); - return -1; - } - } - } - return 0; -} - -extern "C" void __declspec(dllexport) nsisPlugin(HWND hwndParent, int string_size, - char* variables, stack_t** stacktop, - extra_parameters* extra) -{ - //g_hwndParent=hwndParent; - - EXDLL_INIT(); - { - std::string param1(g_stringsize, ' '); - int retVal = popstring(¶m1[0]); - if(retVal == 0) - { - std::string param2(g_stringsize, ' '); - retVal = popstring(¶m2[0]); - if(retVal == 0) - install(param1.c_str(), param2.c_str()); - return; - } - fprintf(stderr, "Not enough parameters.\n"); - } -} - -/* -int _tmain(int argc, _TCHAR* argv[]) -{ - if ( argc<3 ) - { - std::cout << "This program is needed to install/uninstall KDiff3 for clearcase.\n" - "It tries to patch the map file (clearcase-subdir\\lib\\mgrs\\map)\n" - "Usage 1: ccInstHelper install pathToKdiff3.exe\n" - "Usage 2: ccInstHelper uninstall pathToKdiff3.exe\n" - "Backups of the original map files are created in the dir of the map file.\n"; - } - else - { - return install( argv[1], argv[2] ); - } - - return 0; -} -*/ diff --git a/src/fileaccess.cpp b/src/fileaccess.cpp index 8df4a39..55dd661 100644 --- a/src/fileaccess.cpp +++ b/src/fileaccess.cpp @@ -1,1783 +1,1757 @@ /*************************************************************************** * Copyright (C) 2003-2011 by Joachim Eibl * * joachim.eibl at gmx.de * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * ***************************************************************************/ #include "fileaccess.h" #include "common.h" #include "progress.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef Q_OS_WIN #include #include #include #include #else #include // Needed for creating symbolic links via symlink(). #include #endif class FileAccess::Data { public: Data() { reset(); } void reset() { m_url = QUrl(); m_bValidData = false; m_name = QString(); //m_creationTime = QDateTime(); //m_accessTime = QDateTime(); m_bReadable = false; m_bExecutable = false; m_linkTarget = ""; //m_fileType = -1; m_bLocal = true; m_pParent = nullptr; } QUrl m_url; bool m_bLocal; bool m_bValidData; //QDateTime m_accessTime; //QDateTime m_creationTime; bool m_bReadable; bool m_bExecutable; //long m_fileType; // for testing only FileAccess* m_pParent; QString m_linkTarget; //QString m_user; //QString m_group; QString m_name; QString m_localCopy; QString m_statusText; // Might contain an error string, when the last operation didn't succeed. }; FileAccess::FileAccess(const QString& name, bool bWantToWrite) { m_pData = nullptr; m_bUseData = false; setFile(name, bWantToWrite); } FileAccess::FileAccess() { m_bUseData = false; m_bExists = false; m_bFile = false; m_bDir = false; m_bSymLink = false; m_bWritable = false; m_bHidden = false; m_pParent = nullptr; m_size = 0; } FileAccess::FileAccess(const FileAccess& other) { m_pData = nullptr; m_bUseData = false; *this = other; } void FileAccess::createData() { if(d() == nullptr) { FileAccess* pParent = m_pParent; // backup because in union with m_pData m_pData = new Data(); m_bUseData = true; m_pData->m_pParent = pParent; } } FileAccess& FileAccess::operator=(const FileAccess& other) { m_size = other.m_size; m_filePath = other.m_filePath; m_modificationTime = other.m_modificationTime; m_bSymLink = other.m_bSymLink; m_bFile = other.m_bFile; m_bDir = other.m_bDir; m_bExists = other.m_bExists; m_bWritable = other.m_bWritable; m_bHidden = other.m_bHidden; if(other.m_bUseData) { createData(); *m_pData = *other.m_pData; } else { if(m_bUseData) { delete m_pData; } m_bUseData = false; m_pParent = other.parent(); // should be 0 anyway } return *this; } FileAccess::~FileAccess() { if(m_bUseData) { if(!d()->m_localCopy.isEmpty()) { removeTempFile(d()->m_localCopy); } delete m_pData; } } static QString nicePath(const QFileInfo& fi) { QString fp = fi.filePath(); if(fp.length() > 2 && fp[0] == '.' && fp[1] == '/') { return fp.mid(2); } return fp; } // Two kinds of optimization are applied here: // 1. Speed: don't ask for data as long as it is not needed or cheap to get. // When opening a file it is early enough to ask for details. // 2. Memory usage: Don't store data that is not needed, and avoid redundancy. // For recursive directory trees don't store the full path if a parent is available. // Store urls only if files are not local. void FileAccess::setFile(const QFileInfo& fi, FileAccess* pParent) { m_filePath = pParent == nullptr ? fi.absoluteFilePath() : nicePath(fi.filePath()); // remove "./" at start m_bSymLink = fi.isSymLink(); if(m_bSymLink || (!m_bExists && m_filePath.contains("@@"))) { createData(); } if(m_bUseData) d()->m_pParent = pParent; else m_pParent = pParent; if(parent() || d()) // if a parent is specified then we arrive here because of listing a directory { m_bFile = fi.isFile(); m_bDir = fi.isDir(); m_bExists = fi.exists(); m_size = fi.size(); m_modificationTime = fi.lastModified(); m_bHidden = fi.isHidden(); #if defined(Q_OS_WIN) m_bWritable = pParent == 0 || fi.isWritable(); // in certain situations this might become a problem though #else m_bWritable = fi.isWritable(); #endif } if(d() != nullptr) { #if defined(Q_OS_WIN) // On some windows machines in a network this takes very long. // and it's not so important anyway. d()->m_bReadable = true; d()->m_bExecutable = false; #else d()->m_bReadable = fi.isReadable(); d()->m_bExecutable = fi.isExecutable(); #endif //d()->m_creationTime = fi.created(); //d()->m_modificationTime = fi.lastModified(); //d()->m_accessTime = fi.lastRead(); d()->m_name = fi.fileName(); if(m_bSymLink) { d()->m_linkTarget = fi.readLink(); #ifndef Q_OS_WIN // Unfortunately Qt5 symLinkTarget/readLink always returns an absolute path, even if the link is relative char s[PATH_MAX + 1]; ssize_t len = readlink(QFile::encodeName(fi.absoluteFilePath()).constData(), s, PATH_MAX); if(len > 0) { s[len] = '\0'; d()->m_linkTarget = QFile::decodeName(s); } #endif } d()->m_bLocal = true; d()->m_bValidData = true; d()->m_url = QUrl::fromLocalFile(fi.filePath()); if(d()->m_url.isRelative()) { d()->m_url.setPath(absoluteFilePath()); } - - if(!m_bExists && absoluteFilePath().contains("@@")) - { - // Try reading a clearcase file - d()->m_localCopy = FileAccess::tempFileName(); - QString cmd = "cleartool get -to \"" + d()->m_localCopy + "\" \"" + absoluteFilePath() + "\""; - QProcess process; - process.start(cmd); - process.waitForFinished(-1); - //::system( cmd.local8Bit() ); - QFile::setPermissions(d()->m_localCopy, QFile::ReadUser | QFile::WriteUser); // Clearcase creates a write protected file, allow delete. - - QFileInfo fi(d()->m_localCopy); -#if defined(Q_OS_WIN) - d()->m_bReadable = true; //fi.isReadable(); - m_bWritable = true; //fi.isWritable(); - d()->m_bExecutable = false; //fi.isExecutable(); -#else - d()->m_bReadable = fi.isReadable(); - d()->m_bExecutable = fi.isExecutable(); -#endif - //d()->m_creationTime = fi.created(); - //d()->m_accessTime = fi.lastRead(); - m_bExists = fi.exists(); - m_size = fi.size(); - } } } void FileAccess::setFile(const QString& name, bool bWantToWrite) { m_bExists = false; m_bFile = false; m_bDir = false; m_bSymLink = false; m_size = 0; m_modificationTime = QDateTime(); if(d() != nullptr) { d()->reset(); d()->m_pParent = nullptr; } else m_pParent = nullptr; // Note: Checking if the filename-string is empty is necessary for Win95/98/ME. // The isFile() / isDir() queries would cause the program to crash. // (This is a Win95-bug which has been corrected only in WinNT/2000/XP.) if(!name.isEmpty()) { QUrl url = QUrl::fromUserInput(name, QString(), QUrl::AssumeLocalFile); // FileAccess tries to detect if the given name is an URL or a local file. // This is a problem if the filename looks like an URL (i.e. contains a colon ':'). // e.g. "file:f.txt" is a valid filename. // Most of the time it is sufficient to check if the file exists locally. // 2 Problems remain: // 1. When the local file exists and the remote location is wanted nevertheless. (unlikely) // 2. When the local file doesn't exist and should be written to. bool bExistsLocal = QDir().exists(name); if(url.isLocalFile() || url.isRelative() || !url.isValid() || bExistsLocal) // assuming that invalid means relative { QString localName = name; #if defined(Q_OS_WIN) if(localName.startsWith(QLatin1String{"/tmp/"))) { // git on Cygwin will put files in /tmp // A workaround for the a native kdiff3 binary to find them... QString cygwinBin = getenv("CYGWIN_BIN"); if(!cygwinBin.isEmpty()) { localName = QString("%1\\..%2").arg(cygwinBin).arg(name); } } #endif if(!bExistsLocal && url.isLocalFile() && name.left(5).toLower() == "file:") { localName = url.path(); // I want the path without preceding "file:" } QFileInfo fi(localName); setFile(fi, nullptr); } else { createData(); d()->m_url = url; d()->m_name = d()->m_url.fileName(); d()->m_bLocal = false; FileAccessJobHandler jh(this); // A friend, which writes to the parameters of this class! jh.stat(2 /*all details*/, bWantToWrite); // returns bSuccess, ignored m_filePath = name; d()->m_bValidData = true; // After running stat() the variables are initialised // and valid even if the file doesn't exist and the stat // query failed. } } } void FileAccess::addPath(const QString& txt) { if(d() != nullptr && d()->m_url.isValid()) { QUrl url = d()->m_url.adjusted(QUrl::StripTrailingSlash); d()->m_url.setPath(url.path() + '/' + txt); setFile(d()->m_url.url()); // reinitialise } else { QString slash = (txt.isEmpty() || txt[0] == '/') ? "" : "/"; setFile(absoluteFilePath() + slash + txt); } } /* Filetype: S_IFMT 0170000 bitmask for the file type bitfields S_IFSOCK 0140000 socket S_IFLNK 0120000 symbolic link S_IFREG 0100000 regular file S_IFBLK 0060000 block device S_IFDIR 0040000 directory S_IFCHR 0020000 character device S_IFIFO 0010000 fifo S_ISUID 0004000 set UID bit S_ISGID 0002000 set GID bit (see below) S_ISVTX 0001000 sticky bit (see below) Access: S_IRWXU 00700 mask for file owner permissions S_IRUSR 00400 owner has read permission S_IWUSR 00200 owner has write permission S_IXUSR 00100 owner has execute permission S_IRWXG 00070 mask for group permissions S_IRGRP 00040 group has read permission S_IWGRP 00020 group has write permission S_IXGRP 00010 group has execute permission S_IRWXO 00007 mask for permissions for others (not in group) S_IROTH 00004 others have read permission S_IWOTH 00002 others have write permisson S_IXOTH 00001 others have execute permission */ //TODO: Change this to kf5/kde auto test #ifdef Q_OS_WIN void FileAccess::setUdsEntry(const KIO::UDSEntry&) { } // not needed if KDE is not available #else void FileAccess::setUdsEntry(const KIO::UDSEntry& e) { long acc = 0; long fileType = 0; QVector fields = e.fields(); for(QVector::ConstIterator ei = fields.constBegin(); ei != fields.constEnd(); ++ei) { uint f = *ei; switch(f) { case KIO::UDSEntry::UDS_SIZE: m_size = e.numberValue(f); break; //case KIO::UDSEntry::UDS_USER : d()->m_user = e.stringValue(f); break; //case KIO::UDSEntry::UDS_GROUP : d()->m_group = e.stringValue(f); break; case KIO::UDSEntry::UDS_NAME: m_filePath = e.stringValue(f); break; // During listDir the relative path is given here. case KIO::UDSEntry::UDS_MODIFICATION_TIME: m_modificationTime = QDateTime::fromMSecsSinceEpoch(e.numberValue(f)); break; //case KIO::UDSEntry::UDS_ACCESS_TIME : d()->m_accessTime.setTime_t( e.numberValue(f) ); break; //case KIO::UDSEntry::UDS_CREATION_TIME : d()->m_creationTime.setTime_t( e.numberValue(f) ); break; case KIO::UDSEntry::UDS_LINK_DEST: d()->m_linkTarget = e.stringValue(f); break; case KIO::UDSEntry::UDS_ACCESS: { acc = e.numberValue(f); d()->m_bReadable = (acc & S_IRUSR) != 0; m_bWritable = (acc & S_IWUSR) != 0; d()->m_bExecutable = (acc & S_IXUSR) != 0; break; } case KIO::UDSEntry::UDS_FILE_TYPE: { fileType = e.numberValue(f); m_bDir = (fileType & S_IFMT) == S_IFDIR; m_bFile = (fileType & S_IFMT) == S_IFREG; m_bSymLink = (fileType & S_IFMT) == S_IFLNK; m_bExists = fileType != 0; //d()->m_fileType = fileType; break; } case KIO::UDSEntry::UDS_URL: // m_url = QUrlFix( e.stringValue(f) ); break; case KIO::UDSEntry::UDS_MIME_TYPE: break; case KIO::UDSEntry::UDS_GUESSED_MIME_TYPE: break; case KIO::UDSEntry::UDS_XML_PROPERTIES: break; default: break; } } m_bExists = acc != 0 || fileType != 0; d()->m_bLocal = false; d()->m_bValidData = true; m_bSymLink = !d()->m_linkTarget.isEmpty(); if(d()->m_name.isEmpty()) { int pos = m_filePath.lastIndexOf('/') + 1; d()->m_name = m_filePath.mid(pos); } m_bHidden = d()->m_name[0] == '.'; } #endif bool FileAccess::isValid() const { return d() == nullptr ? !m_filePath.isEmpty() : d()->m_bValidData; } bool FileAccess::isFile() const { if(parent() || d()) return m_bFile; else return QFileInfo(absoluteFilePath()).isFile(); } bool FileAccess::isDir() const { if(parent() || d()) return m_bDir; else return QFileInfo(absoluteFilePath()).isDir(); } bool FileAccess::isSymLink() const { return m_bSymLink; } bool FileAccess::exists() const { if(parent() || d()) return m_bExists; else return QFileInfo::exists(absoluteFilePath()); } qint64 FileAccess::size() const { if(parent() || d()) return m_size; else return QFileInfo(absoluteFilePath()).size(); } QUrl FileAccess::url() const { if(d() != nullptr) return d()->m_url; else { QUrl url = QUrl::fromLocalFile(m_filePath); if(url.isRelative()) { url.setPath(absoluteFilePath()); } return url; } } bool FileAccess::isLocal() const { return d() == nullptr || d()->m_bLocal; } bool FileAccess::isReadable() const { #if defined(Q_OS_WIN) // On some windows machines in a network this takes very long to find out and it's not so important anyway. return true; #else if(d() != nullptr) return d()->m_bReadable; else return QFileInfo(absoluteFilePath()).isReadable(); #endif } bool FileAccess::isWritable() const { if(parent() || d()) return m_bWritable; else return QFileInfo(absoluteFilePath()).isWritable(); } bool FileAccess::isExecutable() const { #if defined(Q_OS_WIN) // On some windows machines in a network this takes very long to find out and it's not so important anyway. return true; #else if(d() != nullptr) return d()->m_bExecutable; else return QFileInfo(absoluteFilePath()).isExecutable(); #endif } bool FileAccess::isHidden() const { if(parent() || d()) return m_bHidden; else return QFileInfo(absoluteFilePath()).isHidden(); } QString FileAccess::readLink() const { if(d() != nullptr) return d()->m_linkTarget; else return QString(); } QString FileAccess::absoluteFilePath() const { if(parent() != nullptr) return parent()->absoluteFilePath() + "/" + m_filePath; else { if(m_filePath.isEmpty()) return QString(); if(!isLocal()) return m_filePath; // return complete url QFileInfo fi(m_filePath); if(fi.isAbsolute()) return m_filePath; else return fi.absoluteFilePath(); // Probably never reached } } // Full abs path // Just the name-part of the path, without parent directories QString FileAccess::fileName() const { if(d() != nullptr) return d()->m_name; else if(parent()) return m_filePath; else return QFileInfo(m_filePath).fileName(); } void FileAccess::setSharedName(const QString& name) { if(name == m_filePath) m_filePath = name; // reduce memory because string is only used once. } QString FileAccess::filePath() const { if(parent() && parent()->parent()) return parent()->filePath() + "/" + m_filePath; else return m_filePath; // The path-string that was used during construction } FileAccess* FileAccess::parent() const { if(m_bUseData) return d()->m_pParent; else return m_pParent; } FileAccess::Data* FileAccess::d() { if(m_bUseData) return m_pData; else return nullptr; } const FileAccess::Data* FileAccess::d() const { if(m_bUseData) return m_pData; else return nullptr; } QString FileAccess::prettyAbsPath() const { return isLocal() ? absoluteFilePath() : d()->m_url.toDisplayString(); } /* QDateTime FileAccess::created() const { if ( d()!=0 ) { if ( isLocal() && d()->m_creationTime.isNull() ) const_cast(this)->d()->m_creationTime = QFileInfo( absoluteFilePath() ).created(); return ( d()->m_creationTime.isValid() ? d()->m_creationTime : lastModified() ); } else { QDateTime created = QFileInfo( absoluteFilePath() ).created(); return created.isValid() ? created : lastModified(); } } */ QDateTime FileAccess::lastModified() const { if(isLocal() && m_modificationTime.isNull()) const_cast(this)->m_modificationTime = QFileInfo(absoluteFilePath()).lastModified(); return m_modificationTime; } /* QDateTime FileAccess::lastRead() const { QDateTime accessTime = d()!=0 ? d()->m_accessTime : QFileInfo( absoluteFilePath() ).lastRead(); return ( accessTime.isValid() ? accessTime : lastModified() ); } */ static bool interruptableReadFile(QFile& f, void* pDestBuffer, qint64 maxLength) { ProgressProxy pp; const qint64 maxChunkSize = 100000; qint64 i = 0; pp.setMaxNofSteps(maxLength / maxChunkSize + 1); while(i < maxLength) { qint64 nextLength = min2(maxLength - i, maxChunkSize); qint64 reallyRead = f.read((char*)pDestBuffer + i, nextLength); if(reallyRead != nextLength) { return false; } i += reallyRead; pp.setCurrent(double(i) / maxLength); if(pp.wasCancelled()) return false; } return true; } bool FileAccess::readFile(void* pDestBuffer, qint64 maxLength) { if(d() != nullptr && !d()->m_localCopy.isEmpty()) { QFile f(d()->m_localCopy); if(f.open(QIODevice::ReadOnly)) return interruptableReadFile(f, pDestBuffer, maxLength); // maxLength == f.read( (char*)pDestBuffer, maxLength ); } else if(isLocal()) { QFile f(absoluteFilePath()); if(f.open(QIODevice::ReadOnly)) return interruptableReadFile(f, pDestBuffer, maxLength); //maxLength == f.read( (char*)pDestBuffer, maxLength ); } else { FileAccessJobHandler jh(this); return jh.get(pDestBuffer, maxLength); } return false; } bool FileAccess::writeFile(const void* pSrcBuffer, qint64 length) { ProgressProxy pp; if(isLocal()) { QFile f(absoluteFilePath()); if(f.open(QIODevice::WriteOnly)) { const qint64 maxChunkSize = 100000; pp.setMaxNofSteps(length / maxChunkSize + 1); qint64 i = 0; while(i < length) { qint64 nextLength = min2(length - i, maxChunkSize); qint64 reallyWritten = f.write((char*)pSrcBuffer + i, nextLength); if(reallyWritten != nextLength) { return false; } i += reallyWritten; pp.step(); if(pp.wasCancelled()) return false; } f.close(); #ifndef Q_OS_WIN if(isExecutable()) // value is true if the old file was executable { // Preserve attributes f.setPermissions(f.permissions() | QFile::ExeUser); //struct stat srcFileStatus; //int statResult = ::stat( filePath().toLocal8Bit().constData(), &srcFileStatus ); //if (statResult==0) //{ // ::chmod ( filePath().toLocal8Bit().constData(), srcFileStatus.st_mode | S_IXUSR ); //} } #endif return true; } } else { FileAccessJobHandler jh(this); return jh.put(pSrcBuffer, length, true /*overwrite*/); } return false; } bool FileAccess::copyFile(const QString& dest) { FileAccessJobHandler jh(this); return jh.copyFile(dest); // Handles local and remote copying. } bool FileAccess::rename(const QString& dest) { FileAccessJobHandler jh(this); return jh.rename(dest); } bool FileAccess::removeFile() { if(isLocal()) { return QDir().remove(absoluteFilePath()); } else { FileAccessJobHandler jh(this); return jh.removeFile(url()); } } bool FileAccess::removeFile(const QString& name) // static { return FileAccess(name).removeFile(); } bool FileAccess::listDir(t_DirectoryList* pDirList, bool bRecursive, bool bFindHidden, const QString& filePattern, const QString& fileAntiPattern, const QString& dirAntiPattern, bool bFollowDirLinks, bool bUseCvsIgnore) { FileAccessJobHandler jh(this); return jh.listDir(pDirList, bRecursive, bFindHidden, filePattern, fileAntiPattern, dirAntiPattern, bFollowDirLinks, bUseCvsIgnore); } QString FileAccess::tempFileName() { QTemporaryFile tmpFile; tmpFile.open(); //tmpFile.setAutoDelete( true ); // We only want the name. Delete the precreated file immediately. QString name = tmpFile.fileName() + ".2"; tmpFile.close(); return name; } bool FileAccess::removeTempFile(const QString& name) // static { if(name.endsWith(QLatin1String(".2"))) FileAccess(name.left(name.length() - 2)).removeFile(); return FileAccess(name).removeFile(); } bool FileAccess::makeDir(const QString& dirName) { FileAccessJobHandler fh(nullptr); return fh.mkDir(dirName); } bool FileAccess::removeDir(const QString& dirName) { FileAccessJobHandler fh(nullptr); return fh.rmDir(dirName); } #if defined(Q_OS_WIN) bool FileAccess::symLink(const QString& /*linkTarget*/, const QString& /*linkLocation*/) { return false; } #else bool FileAccess::symLink(const QString& linkTarget, const QString& linkLocation) { return 0 == ::symlink(linkTarget.toLocal8Bit().constData(), linkLocation.toLocal8Bit().constData()); //FileAccessJobHandler fh(0); //return fh.symLink( linkTarget, linkLocation ); } #endif bool FileAccess::exists(const QString& name) { FileAccess fa(name); return fa.exists(); } // If the size couldn't be determined by stat() then the file is copied to a local temp file. qint64 FileAccess::sizeForReading() { if(!isLocal() && m_size == 0) { // Size couldn't be determined. Copy the file to a local temp place. QString localCopy = tempFileName(); bool bSuccess = copyFile(localCopy); if(bSuccess) { QFileInfo fi(localCopy); m_size = fi.size(); d()->m_localCopy = localCopy; return m_size; } else { return 0; } } else return size(); } QString FileAccess::getStatusText() { return d() == nullptr ? QString() : d()->m_statusText; } void FileAccess::setStatusText(const QString& s) { if(!s.isEmpty() || d() != nullptr) { createData(); d()->m_statusText = s; } } QString FileAccess::cleanPath(const QString& path) // static { QUrl url = QUrl::fromUserInput(path, QString(""), QUrl::AssumeLocalFile); if(url.isLocalFile() || !url.isValid()) { return QDir().cleanPath(path); } else { return path; } } bool FileAccess::createBackup(const QString& bakExtension) { if(exists()) { createData(); setFile(absoluteFilePath()); // make sure Data is initialized // First rename the existing file to the bak-file. If a bak-file file exists, delete that. QString bakName = absoluteFilePath() + bakExtension; FileAccess bakFile(bakName, true /*bWantToWrite*/); if(bakFile.exists()) { bool bSuccess = bakFile.removeFile(); if(!bSuccess) { setStatusText(i18n("While trying to make a backup, deleting an older backup failed.\nFilename: %1", bakName)); return false; } } bool bSuccess = rename(bakName); if(!bSuccess) { setStatusText(i18n("While trying to make a backup, renaming failed.\nFilenames: %1 -> %2", absoluteFilePath(), bakName)); return false; } } return true; } FileAccessJobHandler::FileAccessJobHandler(FileAccess* pFileAccess) { m_pFileAccess = pFileAccess; m_bSuccess = false; } bool FileAccessJobHandler::stat(int detail, bool bWantToWrite) { m_bSuccess = false; m_pFileAccess->setStatusText(QString()); KIO::StatJob* pStatJob = KIO::stat(m_pFileAccess->url(), bWantToWrite ? KIO::StatJob::DestinationSide : KIO::StatJob::SourceSide, detail, KIO::HideProgressInfo); connect(pStatJob, &KIO::StatJob::result, this, &FileAccessJobHandler::slotStatResult); ProgressProxy::enterEventLoop(pStatJob, i18n("Getting file status: %1", m_pFileAccess->prettyAbsPath())); return m_bSuccess; } void FileAccessJobHandler::slotStatResult(KJob* pJob) { if(pJob->error()) { //pJob->uiDelegate()->showErrorMessage(); m_pFileAccess->m_bExists = false; m_bSuccess = true; } else { m_bSuccess = true; m_pFileAccess->d()->m_bValidData = true; const KIO::UDSEntry e = static_cast(pJob)->statResult(); m_pFileAccess->setUdsEntry(e); } ProgressProxy::exitEventLoop(); } bool FileAccessJobHandler::get(void* pDestBuffer, long maxLength) { ProgressProxyExtender pp; // Implicitly used in slotPercent() if(maxLength > 0 && !pp.wasCancelled()) { KIO::TransferJob* pJob = KIO::get(m_pFileAccess->url(), KIO::NoReload); m_transferredBytes = 0; m_pTransferBuffer = (char*)pDestBuffer; m_maxLength = maxLength; m_bSuccess = false; m_pFileAccess->setStatusText(QString()); connect(pJob, &KIO::TransferJob::result, this, &FileAccessJobHandler::slotSimpleJobResult); connect(pJob, &KIO::TransferJob::data, this, &FileAccessJobHandler::slotGetData); connect(pJob, SIGNAL(percent(KJob*, qint64)), &pp, SLOT(slotPercent(KJob*, qint64))); ProgressProxy::enterEventLoop(pJob, i18n("Reading file: %1", m_pFileAccess->prettyAbsPath())); return m_bSuccess; } else return true; } void FileAccessJobHandler::slotGetData(KJob* pJob, const QByteArray& newData) { if(pJob->error()) { pJob->uiDelegate()->showErrorMessage(); } else { qint64 length = min2(qint64(newData.size()), m_maxLength - m_transferredBytes); ::memcpy(m_pTransferBuffer + m_transferredBytes, newData.data(), newData.size()); m_transferredBytes += length; } } bool FileAccessJobHandler::put(const void* pSrcBuffer, long maxLength, bool bOverwrite, bool bResume, int permissions) { ProgressProxyExtender pp; // Implicitly used in slotPercent() if(maxLength > 0) { KIO::TransferJob* pJob = KIO::put(m_pFileAccess->url(), permissions, KIO::HideProgressInfo | (bOverwrite ? KIO::Overwrite : KIO::DefaultFlags) | (bResume ? KIO::Resume : KIO::DefaultFlags)); m_transferredBytes = 0; m_pTransferBuffer = (char*)pSrcBuffer; m_maxLength = maxLength; m_bSuccess = false; m_pFileAccess->setStatusText(QString()); connect(pJob, &KIO::TransferJob::result, this, &FileAccessJobHandler::slotPutJobResult); connect(pJob, &KIO::TransferJob::dataReq, this, &FileAccessJobHandler::slotPutData); connect(pJob, SIGNAL(percent(KJob*, qint64)), &pp, SLOT(slotPercent(KJob*, qint64))); ProgressProxy::enterEventLoop(pJob, i18n("Writing file: %1", m_pFileAccess->prettyAbsPath())); return m_bSuccess; } else return true; } void FileAccessJobHandler::slotPutData(KIO::Job* pJob, QByteArray& data) { if(pJob->error()) { pJob->uiDelegate()->showErrorMessage(); } else { /* Think twice before doing this in new code. The maxChunkSize must be able to fit a 32-bit int. Given that the fallowing is safe. */ qint64 maxChunkSize = 100000; qint64 length = min2(maxChunkSize, m_maxLength - m_transferredBytes); data.resize((int)length); if(data.size() == (int)length) { if(length > 0) { ::memcpy(data.data(), m_pTransferBuffer + m_transferredBytes, data.size()); m_transferredBytes += length; } } else { KMessageBox::error(ProgressProxy::getDialog(), i18n("Out of memory")); data.resize(0); m_bSuccess = false; } } } void FileAccessJobHandler::slotPutJobResult(KJob* pJob) { if(pJob->error()) { pJob->uiDelegate()->showErrorMessage(); } else { m_bSuccess = (m_transferredBytes == m_maxLength); // Special success condition } ProgressProxy::exitEventLoop(); // Close the dialog, return from exec() } bool FileAccessJobHandler::mkDir(const QString& dirName) { QUrl dirURL = QUrl::fromUserInput(dirName, QString(""), QUrl::AssumeLocalFile); if(dirName.isEmpty()) return false; else if(dirURL.isLocalFile() || dirURL.isRelative()) { return QDir().mkdir(dirURL.path()); } else { m_bSuccess = false; KIO::SimpleJob* pJob = KIO::mkdir(dirURL); connect(pJob, &KIO::SimpleJob::result, this, &FileAccessJobHandler::slotSimpleJobResult); ProgressProxy::enterEventLoop(pJob, i18n("Making directory: %1", dirName)); return m_bSuccess; } } bool FileAccessJobHandler::rmDir(const QString& dirName) { QUrl dirURL = QUrl::fromUserInput(dirName, QString(""), QUrl::AssumeLocalFile); if(dirName.isEmpty()) return false; else if(dirURL.isLocalFile()) { return QDir().rmdir(dirURL.path()); } else { m_bSuccess = false; KIO::SimpleJob* pJob = KIO::rmdir(dirURL); connect(pJob, &KIO::SimpleJob::result, this, &FileAccessJobHandler::slotSimpleJobResult); ProgressProxy::enterEventLoop(pJob, i18n("Removing directory: %1", dirName)); return m_bSuccess; } } bool FileAccessJobHandler::removeFile(const QUrl& fileName) { if(fileName.isEmpty()) return false; else { m_bSuccess = false; KIO::SimpleJob* pJob = KIO::file_delete(fileName, KIO::HideProgressInfo); connect(pJob, &KIO::SimpleJob::result, this, &FileAccessJobHandler::slotSimpleJobResult); ProgressProxy::enterEventLoop(pJob, i18n("Removing file: %1", fileName.toDisplayString())); return m_bSuccess; } } bool FileAccessJobHandler::symLink(const QUrl& linkTarget, const QUrl& linkLocation) { if(linkTarget.isEmpty() || linkLocation.isEmpty()) return false; else { m_bSuccess = false; KIO::CopyJob* pJob = KIO::link(linkTarget, linkLocation, KIO::HideProgressInfo); connect(pJob, &KIO::CopyJob::result, this, &FileAccessJobHandler::slotSimpleJobResult); ProgressProxy::enterEventLoop(pJob, i18n("Creating symbolic link: %1 -> %2", linkLocation.toDisplayString(), linkTarget.toDisplayString())); return m_bSuccess; } } bool FileAccessJobHandler::rename(const QString& dest) { if(dest.isEmpty()) return false; QUrl kurl = QUrl::fromUserInput(dest, QString(""), QUrl::AssumeLocalFile); if(kurl.isRelative()) kurl = QUrl::fromUserInput(QDir().absoluteFilePath(dest), QString(""), QUrl::AssumeLocalFile); // assuming that invalid means relative if(m_pFileAccess->isLocal() && kurl.isLocalFile()) { return QDir().rename(m_pFileAccess->absoluteFilePath(), kurl.path()); } else { ProgressProxyExtender pp; int permissions = -1; m_bSuccess = false; KIO::FileCopyJob* pJob = KIO::file_move(m_pFileAccess->url(), kurl, permissions, KIO::HideProgressInfo); connect(pJob, &KIO::FileCopyJob::result, this, &FileAccessJobHandler::slotSimpleJobResult); connect(pJob, SIGNAL(percent(KJob*, qint64)), &pp, SLOT(slotPercent(KJob*, qint64))); ProgressProxy::enterEventLoop(pJob, i18n("Renaming file: %1 -> %2", m_pFileAccess->prettyAbsPath(), dest)); return m_bSuccess; } } void FileAccessJobHandler::slotSimpleJobResult(KJob* pJob) { if(pJob->error()) { pJob->uiDelegate()->showErrorMessage(); } else { m_bSuccess = true; } ProgressProxy::exitEventLoop(); // Close the dialog, return from exec() } // Copy local or remote files. bool FileAccessJobHandler::copyFile(const QString& dest) { ProgressProxyExtender pp; QUrl destUrl = QUrl::fromUserInput(dest, QString(""), QUrl::AssumeLocalFile); m_pFileAccess->setStatusText(QString()); if(!m_pFileAccess->isLocal() || !destUrl.isLocalFile()) // if either url is nonlocal { int permissions = (m_pFileAccess->isExecutable() ? 0111 : 0) + (m_pFileAccess->isWritable() ? 0222 : 0) + (m_pFileAccess->isReadable() ? 0444 : 0); m_bSuccess = false; KIO::FileCopyJob* pJob = KIO::file_copy(m_pFileAccess->url(), destUrl, permissions, KIO::HideProgressInfo); connect(pJob, &KIO::FileCopyJob::result, this, &FileAccessJobHandler::slotSimpleJobResult); connect(pJob, SIGNAL(percent(KJob*, qint64)), &pp, SLOT(slotPercent(KJob*, qint64))); ProgressProxy::enterEventLoop(pJob, i18n("Copying file: %1 -> %2", m_pFileAccess->prettyAbsPath(), dest)); return m_bSuccess; // Note that the KIO-slave preserves the original date, if this is supported. } // Both files are local: QString srcName = m_pFileAccess->absoluteFilePath(); QString destName = dest; QFile srcFile(srcName); QFile destFile(destName); bool bReadSuccess = srcFile.open(QIODevice::ReadOnly); if(bReadSuccess == false) { m_pFileAccess->setStatusText(i18n("Error during file copy operation: Opening file for reading failed. Filename: %1", srcName)); return false; } bool bWriteSuccess = destFile.open(QIODevice::WriteOnly); if(bWriteSuccess == false) { m_pFileAccess->setStatusText(i18n("Error during file copy operation: Opening file for writing failed. Filename: %1", destName)); return false; } std::vector buffer(100000); qint64 bufSize = buffer.size(); qint64 srcSize = srcFile.size(); while(srcSize > 0 && !pp.wasCancelled()) { qint64 readSize = srcFile.read(&buffer[0], min2(srcSize, bufSize)); if(readSize == -1 || readSize == 0) { m_pFileAccess->setStatusText(i18n("Error during file copy operation: Reading failed. Filename: %1", srcName)); return false; } srcSize -= readSize; while(readSize > 0) { qint64 writeSize = destFile.write(&buffer[0], readSize); if(writeSize == -1 || writeSize == 0) { m_pFileAccess->setStatusText(i18n("Error during file copy operation: Writing failed. Filename: %1", destName)); return false; } readSize -= writeSize; } destFile.flush(); pp.setCurrent((double)(srcFile.size() - srcSize) / srcFile.size(), false); } srcFile.close(); destFile.close(); // Update the times of the destFile #ifdef Q_OS_WIN struct _stat srcFileStatus; int statResult = ::_stat(srcName.toLocal8Bit().constData(), &srcFileStatus); if(statResult == 0) { _utimbuf destTimes; destTimes.actime = srcFileStatus.st_atime; /* time of last access */ destTimes.modtime = srcFileStatus.st_mtime; /* time of last modification */ _utime(destName.toLocal8Bit().constData(), &destTimes); _chmod(destName.toLocal8Bit().constData(), srcFileStatus.st_mode); } #else struct stat srcFileStatus; int statResult = ::stat(srcName.toLocal8Bit().constData(), &srcFileStatus); if(statResult == 0) { utimbuf destTimes; destTimes.actime = srcFileStatus.st_atime; /* time of last access */ destTimes.modtime = srcFileStatus.st_mtime; /* time of last modification */ utime(destName.toLocal8Bit().constData(), &destTimes); chmod(destName.toLocal8Bit().constData(), srcFileStatus.st_mode); } #endif return true; } bool wildcardMultiMatch(const QString& wildcard, const QString& testString, bool bCaseSensitive) { static QHash s_patternMap; QStringList sl = wildcard.split(";"); for(QStringList::Iterator it = sl.begin(); it != sl.end(); ++it) { QHash::iterator patIt = s_patternMap.find(*it); if(patIt == s_patternMap.end()) { QRegExp pattern(*it, bCaseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive, QRegExp::Wildcard); patIt = s_patternMap.insert(*it, pattern); } if(patIt.value().exactMatch(testString)) return true; } return false; } // class CvsIgnoreList from Cervisia cvsdir.cpp // Copyright (C) 1999-2002 Bernd Gehrmann // with elements from class StringMatcher // Copyright (c) 2003 Andre Woebbeking // Modifications for KDiff3 by Joachim Eibl class CvsIgnoreList { public: CvsIgnoreList() {} void init(FileAccess& dir, bool bUseLocalCvsIgnore); bool matches(const QString& fileName, bool bCaseSensitive) const; private: void addEntriesFromString(const QString& str); void addEntriesFromFile(const QString& name); void addEntry(const QString& entry); QStringList m_exactPatterns; QStringList m_startPatterns; QStringList m_endPatterns; QStringList m_generalPatterns; }; void CvsIgnoreList::init(FileAccess& dir, bool bUseLocalCvsIgnore) { static const char* ignorestr = ". .. core RCSLOG tags TAGS RCS SCCS .make.state " ".nse_depinfo #* .#* cvslog.* ,* CVS CVS.adm .del-* *.a *.olb *.o *.obj " "*.so *.Z *~ *.old *.elc *.ln *.bak *.BAK *.orig *.rej *.exe _$* *$"; addEntriesFromString(QString::fromLatin1(ignorestr)); addEntriesFromFile(QDir::homePath() + "/.cvsignore"); addEntriesFromString(QString::fromLocal8Bit(::getenv("CVSIGNORE"))); if(bUseLocalCvsIgnore) { FileAccess file(dir); file.addPath(".cvsignore"); qint64 size = file.exists() ? file.sizeForReading() : 0; if(size > 0) { char* buf = new char[size]; if(buf != nullptr) { file.readFile(buf, size); int pos1 = 0; for(int pos = 0; pos <= size; ++pos) { if(pos == size || buf[pos] == ' ' || buf[pos] == '\t' || buf[pos] == '\n' || buf[pos] == '\r') { if(pos > pos1) { addEntry(QString::fromLatin1(&buf[pos1], pos - pos1)); } ++pos1; } } delete[] buf; } } } } void CvsIgnoreList::addEntriesFromString(const QString& str) { int posLast(0); int pos; while((pos = str.indexOf(' ', posLast)) >= 0) { if(pos > posLast) addEntry(str.mid(posLast, pos - posLast)); posLast = pos + 1; } if(posLast < static_cast(str.length())) addEntry(str.mid(posLast)); } void CvsIgnoreList::addEntriesFromFile(const QString& name) { QFile file(name); if(file.open(QIODevice::ReadOnly)) { QTextStream stream(&file); while(!stream.atEnd()) { addEntriesFromString(stream.readLine()); } } } void CvsIgnoreList::addEntry(const QString& pattern) { if(pattern != QString("!")) { if(pattern.isEmpty()) return; // The general match is general but slow. // Special tests for '*' and '?' at the beginning or end of a pattern // allow fast checks. // Count number of '*' and '?' unsigned int nofMetaCharacters = 0; const QChar* pos; pos = pattern.unicode(); const QChar* posEnd; posEnd = pos + pattern.length(); while(pos < posEnd) { if(*pos == QChar('*') || *pos == QChar('?')) ++nofMetaCharacters; ++pos; } if(nofMetaCharacters == 0) { m_exactPatterns.append(pattern); } else if(nofMetaCharacters == 1) { if(pattern.at(0) == QChar('*')) { m_endPatterns.append(pattern.right(pattern.length() - 1)); } else if(pattern.at(pattern.length() - 1) == QChar('*')) { m_startPatterns.append(pattern.left(pattern.length() - 1)); } else { m_generalPatterns.append(pattern.toLocal8Bit()); } } else { m_generalPatterns.append(pattern.toLocal8Bit()); } } else { m_exactPatterns.clear(); m_startPatterns.clear(); m_endPatterns.clear(); m_generalPatterns.clear(); } } bool CvsIgnoreList::matches(const QString& text, bool bCaseSensitive) const { if(m_exactPatterns.indexOf(text) >= 0) { return true; } QStringList::ConstIterator it; QStringList::ConstIterator itEnd; for(it = m_startPatterns.begin(), itEnd = m_startPatterns.end(); it != itEnd; ++it) { if(text.startsWith(*it)) { return true; } } for(it = m_endPatterns.begin(), itEnd = m_endPatterns.end(); it != itEnd; ++it) { if(text.mid(text.length() - (*it).length()) == *it) //(text.endsWith(*it)) { return true; } } /* for (QValueList::const_iterator it(m_generalPatterns.begin()), itEnd(m_generalPatterns.end()); it != itEnd; ++it) { if (::fnmatch(*it, text.local8Bit(), FNM_PATHNAME) == 0) { return true; } } */ for(it = m_generalPatterns.begin(); it != m_generalPatterns.end(); ++it) { QRegExp pattern(*it, bCaseSensitive ? Qt::CaseSensitive : Qt::CaseInsensitive, QRegExp::Wildcard); if(pattern.exactMatch(text)) return true; } return false; } static bool cvsIgnoreExists(t_DirectoryList* pDirList) { t_DirectoryList::iterator i; for(i = pDirList->begin(); i != pDirList->end(); ++i) { if(i->fileName() == ".cvsignore") return true; } return false; } bool FileAccessJobHandler::listDir(t_DirectoryList* pDirList, bool bRecursive, bool bFindHidden, const QString& filePattern, const QString& fileAntiPattern, const QString& dirAntiPattern, bool bFollowDirLinks, bool bUseCvsIgnore) { ProgressProxyExtender pp; m_pDirList = pDirList; m_pDirList->clear(); m_bFindHidden = bFindHidden; m_bRecursive = bRecursive; m_bFollowDirLinks = bFollowDirLinks; // Only relevant if bRecursive==true. m_fileAntiPattern = fileAntiPattern; m_filePattern = filePattern; m_dirAntiPattern = dirAntiPattern; if(pp.wasCancelled()) return true; // Cancelled is not an error. pp.setInformation(i18n("Reading directory: %1", m_pFileAccess->absoluteFilePath()), 0, false); if(m_pFileAccess->isLocal()) { QString currentPath = QDir::currentPath(); m_bSuccess = QDir::setCurrent(m_pFileAccess->absoluteFilePath()); if(m_bSuccess) { #ifndef Q_OS_WIN m_bSuccess = true; QDir dir("."); dir.setSorting(QDir::Name | QDir::DirsFirst); dir.setFilter(QDir::Files | QDir::Dirs | /* from KDE3 QDir::TypeMaskDirs | */ QDir::Hidden | QDir::System); QFileInfoList fiList = dir.entryInfoList(); if(fiList.isEmpty()) { // No Permission to read directory or other error. m_bSuccess = false; } else { foreach(const QFileInfo& fi, fiList) // for each file... { if(fi.fileName() == "." || fi.fileName() == "..") continue; FileAccess fa; fa.setFile(fi, m_pFileAccess); pDirList->push_back(fa); } } #else QString pattern = "*.*"; WIN32_FIND_DATA findData; Qt::HANDLE searchHandle = FindFirstFileW((const wchar_t*)pattern.utf16(), &findData); if(searchHandle != INVALID_HANDLE_VALUE) { QString absPath = m_pFileAccess->absoluteFilePath(); QString relPath = m_pFileAccess->filePath(); bool bFirst = true; while(!pp.wasCancelled()) { if(!bFirst) { if(!FindNextFileW(searchHandle, &findData)) break; } bFirst = false; FileAccess fa; fa.m_filePath = QString::fromUtf16((const ushort*)findData.cFileName); if(fa.m_filePath != "." && fa.m_filePath != "..") { fa.m_size = (qint64(findData.nFileSizeHigh) << 32) + findData.nFileSizeLow; FILETIME ft; SYSTEMTIME t; FileTimeToLocalFileTime(&findData.ftLastWriteTime, &ft); FileTimeToSystemTime(&ft, &t); fa.m_modificationTime = QDateTime(QDate(t.wYear, t.wMonth, t.wDay), QTime(t.wHour, t.wMinute, t.wSecond)); //FileTimeToLocalFileTime( &findData.ftLastAccessTime, &ft ); FileTimeToSystemTime(&ft,&t); //fa.m_accessTime = QDateTime( QDate(t.wYear, t.wMonth, t.wDay), QTime(t.wHour, t.wMinute, t.wSecond) ); //FileTimeToLocalFileTime( &findData.ftCreationTime, &ft ); FileTimeToSystemTime(&ft,&t); //fa.m_creationTime = QDateTime( QDate(t.wYear, t.wMonth, t.wDay), QTime(t.wHour, t.wMinute, t.wSecond) ); int a = findData.dwFileAttributes; fa.m_bWritable = (a & FILE_ATTRIBUTE_READONLY) == 0; fa.m_bDir = (a & FILE_ATTRIBUTE_DIRECTORY) != 0; fa.m_bFile = !fa.m_bDir; fa.m_bHidden = (a & FILE_ATTRIBUTE_HIDDEN) != 0; //fa.m_bExecutable = false; // Useless on windows fa.m_bExists = true; //fa.m_bReadable = true; //fa.m_bLocal = true; //fa.m_bValidData = true; fa.m_bSymLink = false; //fa.m_fileType = 0; //fa.m_filePath = fa.m_name; //fa.m_absoluteFilePath = absPath + "/" + fa.m_name; //fa.m_url.setPath( fa.m_absoluteFilePath ); if(fa.d()) fa.m_pData->m_pParent = m_pFileAccess; else fa.m_pParent = m_pFileAccess; pDirList->push_back(fa); } } FindClose(searchHandle); } else { QDir::setCurrent(currentPath); // restore current path return false; } #endif } QDir::setCurrent(currentPath); // restore current path } else { KIO::ListJob* pListJob = nullptr; pListJob = KIO::listDir(m_pFileAccess->url(), KIO::HideProgressInfo, true /*bFindHidden*/); m_bSuccess = false; if(pListJob != nullptr) { connect(pListJob, &KIO::ListJob::entries, this, &FileAccessJobHandler::slotListDirProcessNewEntries); connect(pListJob, &KIO::ListJob::result, this, &FileAccessJobHandler::slotSimpleJobResult); connect(pListJob, &KIO::ListJob::infoMessage, &pp, &ProgressProxyExtender::slotListDirInfoMessage); // This line makes the transfer via fish unreliable.:-( //connect( pListJob, SIGNAL(percent(KJob*,qint64)), &pp, SLOT(slotPercent(KJob*, qint64))); ProgressProxy::enterEventLoop(pListJob, i18n("Listing directory: %1", m_pFileAccess->prettyAbsPath())); } } CvsIgnoreList cvsIgnoreList; if(bUseCvsIgnore) { cvsIgnoreList.init(*m_pFileAccess, cvsIgnoreExists(pDirList)); } #if defined(Q_OS_WIN) bool bCaseSensitive = false; #else bool bCaseSensitive = true; #endif // Now remove all entries that should be ignored: t_DirectoryList::iterator i; for(i = pDirList->begin(); i != pDirList->end();) { t_DirectoryList::iterator i2 = i; ++i2; QString fn = i->fileName(); if((!bFindHidden && i->isHidden()) || (i->isFile() && (!wildcardMultiMatch(filePattern, fn, bCaseSensitive) || wildcardMultiMatch(fileAntiPattern, fn, bCaseSensitive))) || (i->isDir() && wildcardMultiMatch(dirAntiPattern, fn, bCaseSensitive)) || cvsIgnoreList.matches(fn, bCaseSensitive)) { // Remove it pDirList->erase(i); i = i2; } else { ++i; } } if(bRecursive) { t_DirectoryList subDirsList; t_DirectoryList::iterator i; for(i = m_pDirList->begin(); i != m_pDirList->end(); ++i) { if(i->isDir() && (!i->isSymLink() || m_bFollowDirLinks)) { t_DirectoryList dirList; i->listDir(&dirList, bRecursive, bFindHidden, filePattern, fileAntiPattern, dirAntiPattern, bFollowDirLinks, bUseCvsIgnore); t_DirectoryList::iterator j; for(j = dirList.begin(); j != dirList.end(); ++j) { if(j->parent() == nullptr) j->m_filePath = i->fileName() + "/" + j->m_filePath; } // append data onto the main list subDirsList.splice(subDirsList.end(), dirList); } } m_pDirList->splice(m_pDirList->end(), subDirsList); } return m_bSuccess; } void FileAccessJobHandler::slotListDirProcessNewEntries(KIO::Job*, const KIO::UDSEntryList& l) { //This function is called for non-local urls. Don't use QUrl::fromLocalFile here as it does not handle these. QUrl parentUrl = QUrl::fromUserInput(m_pFileAccess->absoluteFilePath(), QString(""), QUrl::AssumeLocalFile); KIO::UDSEntryList::ConstIterator i; for(i = l.begin(); i != l.end(); ++i) { const KIO::UDSEntry& e = *i; FileAccess fa; fa.createData(); fa.m_pData->m_pParent = m_pFileAccess; fa.setUdsEntry(e); if(fa.fileName() != "." && fa.fileName() != "..") { fa.d()->m_url = parentUrl; QUrl url = fa.d()->m_url.adjusted(QUrl::StripTrailingSlash); fa.d()->m_url.setPath(url.path() + "/" + fa.fileName()); //fa.d()->m_absoluteFilePath = fa.url().url(); m_pDirList->push_back(fa); } } } void ProgressProxyExtender::slotListDirInfoMessage(KJob*, const QString& msg) { setInformation(msg, 0); } void ProgressProxyExtender::slotPercent(KJob*, qint64 percent) { setCurrent(percent); } //#include "fileaccess.moc" diff --git a/src/optiondialog.cpp b/src/optiondialog.cpp index c57422b..fcc9d80 100644 --- a/src/optiondialog.cpp +++ b/src/optiondialog.cpp @@ -1,1904 +1,1858 @@ /* * kdiff3 - Text Diff And Merge Tool * Copyright (C) 2002-2009 Joachim Eibl, joachim.eibl at gmx.de * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "diff.h" #include "optiondialog.h" #include "smalldialogs.h" #define KDIFF3_CONFIG_GROUP "KDiff3 Options" QString s_historyEntryStartRegExpToolTip; QString s_historyEntryStartSortKeyOrderToolTip; QString s_autoMergeRegExpToolTip; QString s_historyStartRegExpToolTip; void OptionDialog::addOptionItem(OptionItem* p) { m_optionItemList.push_back(p); } class OptionItem { public: OptionItem(OptionDialog* pOptionDialog, const QString& saveName) { Q_ASSERT(pOptionDialog != nullptr); pOptionDialog->addOptionItem(this); m_saveName = saveName; m_bPreserved = false; } virtual ~OptionItem() {} virtual void setToDefault() = 0; virtual void setToCurrent() = 0; virtual void apply() = 0; virtual void write(ValueMap*) = 0; virtual void read(ValueMap*) = 0; void doPreserve() { if(!m_bPreserved) { m_bPreserved = true; preserve(); } } void doUnpreserve() { if(m_bPreserved) { unpreserve(); } } QString getSaveName() { return m_saveName; } protected: virtual void preserve() = 0; virtual void unpreserve() = 0; bool m_bPreserved; QString m_saveName; }; template class OptionItemT : public OptionItem { public: OptionItemT(OptionDialog* pOptionDialog, const QString& saveName) : OptionItem(pOptionDialog, saveName) { } protected: void preserve() override { m_preservedVal = *m_pVar; } void unpreserve() override { *m_pVar = m_preservedVal; } T* m_pVar; T m_preservedVal; T m_defaultVal; }; class OptionCheckBox : public QCheckBox, public OptionItemT { public: OptionCheckBox(QString text, bool bDefaultVal, const QString& saveName, bool* pbVar, QWidget* pParent, OptionDialog* pOD) : QCheckBox(text, pParent), OptionItemT(pOD, saveName) { m_pVar = pbVar; m_defaultVal = bDefaultVal; } void setToDefault() override { setChecked(m_defaultVal); } void setToCurrent() override { setChecked(*m_pVar); } void apply() override { *m_pVar = isChecked(); } void write(ValueMap* config) override { config->writeEntry(m_saveName, *m_pVar); } void read(ValueMap* config) override { *m_pVar = config->readBoolEntry(m_saveName, *m_pVar); } private: OptionCheckBox(const OptionCheckBox&); // private copy constructor without implementation }; class OptionRadioButton : public QRadioButton, public OptionItemT { public: OptionRadioButton(QString text, bool bDefaultVal, const QString& saveName, bool* pbVar, QWidget* pParent, OptionDialog* pOD) : QRadioButton(text, pParent), OptionItemT(pOD, saveName) { m_pVar = pbVar; m_defaultVal = bDefaultVal; } void setToDefault() override { setChecked(m_defaultVal); } void setToCurrent() override { setChecked(*m_pVar); } void apply() override { *m_pVar = isChecked(); } void write(ValueMap* config) override { config->writeEntry(m_saveName, *m_pVar); } void read(ValueMap* config) override { *m_pVar = config->readBoolEntry(m_saveName, *m_pVar); } private: OptionRadioButton(const OptionRadioButton&); // private copy constructor without implementation }; template class OptionT : public OptionItemT { public: OptionT(const T& defaultVal, const QString& saveName, T* pVar, OptionDialog* pOD) : OptionItemT(pOD, saveName) { this->m_pVar = pVar; *this->m_pVar = defaultVal; } OptionT(const QString& saveName, T* pVar, OptionDialog* pOD) : OptionItemT(pOD, saveName) { this->m_pVar = pVar; } void setToDefault() override {} void setToCurrent() override {} void apply() override {} void write(ValueMap* vm) override { writeEntry(vm, this->m_saveName, *this->m_pVar); } void read(ValueMap* vm) override { *this->m_pVar = vm->readEntry(this->m_saveName, *this->m_pVar); } private: OptionT(const OptionT&); // private copy constructor without implementation }; template void writeEntry(ValueMap* vm, const QString& saveName, const T& v) { vm->writeEntry(saveName, v); } static void writeEntry(ValueMap* vm, const QString& saveName, const QStringList& v) { vm->writeEntry(saveName, v); } //static void readEntry(ValueMap* vm, const QString& saveName, bool& v ) { v = vm->readBoolEntry( saveName, v ); } //static void readEntry(ValueMap* vm, const QString& saveName, int& v ) { v = vm->readNumEntry( saveName, v ); } //static void readEntry(ValueMap* vm, const QString& saveName, QSize& v ) { v = vm->readSizeEntry( saveName, &v ); } //static void readEntry(ValueMap* vm, const QString& saveName, QPoint& v ) { v = vm->readPointEntry( saveName, &v ); } //static void readEntry(ValueMap* vm, const QString& saveName, QStringList& v ){ v = vm->readListEntry( saveName, QStringList(), '|' ); } typedef OptionT OptionToggleAction; typedef OptionT OptionNum; typedef OptionT OptionPoint; typedef OptionT OptionSize; typedef OptionT OptionStringList; FontChooser::FontChooser(QWidget* pParent) : QGroupBox(pParent) { QVBoxLayout* pLayout = new QVBoxLayout(this); m_pLabel = new QLabel(QString(), this); pLayout->addWidget(m_pLabel); QChar visualTab(0x2192); QChar visualSpace((ushort)0xb7); m_pExampleTextEdit = new QPlainTextEdit(QString("The quick brown fox jumps over the river\n" "but the little red hen escapes with a shiver.\n" ":-)") + visualTab + visualSpace, this); m_pExampleTextEdit->setFont(m_font); m_pExampleTextEdit->setReadOnly(true); pLayout->addWidget(m_pExampleTextEdit); m_pSelectFont = new QPushButton(i18n("Change Font"), this); m_pSelectFont->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); connect(m_pSelectFont, SIGNAL(clicked()), this, SLOT(slotSelectFont())); pLayout->addWidget(m_pSelectFont); pLayout->setAlignment(m_pSelectFont, Qt::AlignRight); } QFont FontChooser::font() { return m_font; //QFont("courier",10); } void FontChooser::setFont(const QFont& font, bool) { m_font = font; m_pExampleTextEdit->setFont(m_font); m_pLabel->setText(i18n("Font: %1, %2, %3\n\nExample:", m_font.family(), m_font.styleName(), m_font.pointSize())); //update(); } void FontChooser::slotSelectFont() { bool bOk; m_font = QFontDialog::getFont(&bOk, m_font); m_pExampleTextEdit->setFont(m_font); m_pLabel->setText(i18n("Font: %1, %2, %3\n\nExample:", m_font.family(), m_font.styleName(), m_font.pointSize())); } class OptionFontChooser : public FontChooser, public OptionItemT { public: OptionFontChooser(const QFont& defaultVal, const QString& saveName, QFont* pVar, QWidget* pParent, OptionDialog* pOD) : FontChooser(pParent), OptionItemT(pOD, saveName) { m_pVar = pVar; *m_pVar = defaultVal; m_defaultVal = defaultVal; } void setToDefault() override { setFont(m_defaultVal, false); } void setToCurrent() override { setFont(*m_pVar, false); } void apply() override { *m_pVar = font(); } void write(ValueMap* config) override { config->writeEntry(m_saveName, *m_pVar); } void read(ValueMap* config) override { *m_pVar = config->readFontEntry(m_saveName, m_pVar); } private: OptionFontChooser(const OptionToggleAction&); // private copy constructor without implementation }; class OptionColorButton : public KColorButton, public OptionItemT { public: OptionColorButton(QColor defaultVal, const QString& saveName, QColor* pVar, QWidget* pParent, OptionDialog* pOD) : KColorButton(pParent), OptionItemT(pOD, saveName) { m_pVar = pVar; m_defaultVal = defaultVal; } void setToDefault() override { setColor(m_defaultVal); } void setToCurrent() override { setColor(*m_pVar); } void apply() override { *m_pVar = color(); } void write(ValueMap* config) override { config->writeEntry(m_saveName, *m_pVar); } void read(ValueMap* config) override { *m_pVar = config->readColorEntry(m_saveName, m_pVar); } private: OptionColorButton(const OptionColorButton&); // private copy constructor without implementation }; class OptionLineEdit : public QComboBox, public OptionItemT { public: OptionLineEdit(const QString& defaultVal, const QString& saveName, QString* pVar, QWidget* pParent, OptionDialog* pOD) : QComboBox(pParent), OptionItemT(pOD, saveName) { setMinimumWidth(50); setEditable(true); m_pVar = pVar; m_defaultVal = defaultVal; m_list.push_back(defaultVal); insertText(); } void setToDefault() override { setEditText(m_defaultVal); } void setToCurrent() override { setEditText(*m_pVar); } void apply() override { *m_pVar = currentText(); insertText(); } void write(ValueMap* config) override { config->writeEntry(m_saveName, m_list); } void read(ValueMap* config) override { m_list = config->readListEntry(m_saveName, QStringList(m_defaultVal)); if(!m_list.empty()) *m_pVar = m_list.front(); clear(); insertItems(0, m_list); } private: void insertText() { // Check if the text exists. If yes remove it and push it in as first element QString current = currentText(); m_list.removeAll(current); m_list.push_front(current); clear(); if(m_list.size() > 10) m_list.erase(m_list.begin() + 10, m_list.end()); insertItems(0, m_list); } OptionLineEdit(const OptionLineEdit&); // private copy constructor without implementation QStringList m_list; }; #if defined QT_NO_VALIDATOR #error No validator #endif class OptionIntEdit : public QLineEdit, public OptionItemT { public: OptionIntEdit(int defaultVal, const QString& saveName, int* pVar, int rangeMin, int rangeMax, QWidget* pParent, OptionDialog* pOD) : QLineEdit(pParent), OptionItemT(pOD, saveName) { m_pVar = pVar; m_defaultVal = defaultVal; QIntValidator* v = new QIntValidator(this); v->setRange(rangeMin, rangeMax); setValidator(v); } void setToDefault() override { QString s; s.setNum(m_defaultVal); setText(s); } void setToCurrent() override { QString s; s.setNum(*m_pVar); setText(s); } void apply() override { const QIntValidator* v = static_cast(validator()); *m_pVar = minMaxLimiter(text().toInt(), v->bottom(), v->top()); setText(QString::number(*m_pVar)); } void write(ValueMap* config) override { config->writeEntry(m_saveName, *m_pVar); } void read(ValueMap* config) override { *m_pVar = config->readNumEntry(m_saveName, *m_pVar); } private: OptionIntEdit(const OptionIntEdit&); // private copy constructor without implementation }; class OptionComboBox : public QComboBox, public OptionItem { public: OptionComboBox(int defaultVal, const QString& saveName, int* pVarNum, QWidget* pParent, OptionDialog* pOD) : QComboBox(pParent), OptionItem(pOD, saveName) { setMinimumWidth(50); m_pVarNum = pVarNum; m_pVarStr = nullptr; m_defaultVal = defaultVal; setEditable(false); } OptionComboBox(int defaultVal, const QString& saveName, QString* pVarStr, QWidget* pParent, OptionDialog* pOD) : QComboBox(pParent), OptionItem(pOD, saveName) { m_pVarNum = nullptr; m_pVarStr = pVarStr; m_defaultVal = defaultVal; setEditable(false); } void setToDefault() override { setCurrentIndex(m_defaultVal); if(m_pVarStr != nullptr) { *m_pVarStr = currentText(); } } void setToCurrent() override { if(m_pVarNum != nullptr) setCurrentIndex(*m_pVarNum); else setText(*m_pVarStr); } void apply() override { if(m_pVarNum != nullptr) { *m_pVarNum = currentIndex(); } else { *m_pVarStr = currentText(); } } void write(ValueMap* config) override { if(m_pVarStr != nullptr) config->writeEntry(m_saveName, *m_pVarStr); else config->writeEntry(m_saveName, *m_pVarNum); } void read(ValueMap* config) override { if(m_pVarStr != nullptr) setText(config->readEntry(m_saveName, currentText())); else *m_pVarNum = config->readNumEntry(m_saveName, *m_pVarNum); } void preserve() override { if(m_pVarStr != nullptr) { m_preservedStrVal = *m_pVarStr; } else { m_preservedNumVal = *m_pVarNum; } } void unpreserve() override { if(m_pVarStr != nullptr) { *m_pVarStr = m_preservedStrVal; } else { *m_pVarNum = m_preservedNumVal; } } private: OptionComboBox(const OptionIntEdit&); // private copy constructor without implementation int* m_pVarNum; int m_preservedNumVal = 0; QString* m_pVarStr; QString m_preservedStrVal; int m_defaultVal; void setText(const QString& s) { // Find the string in the combobox-list, don't change the value if nothing fits. for(int i = 0; i < count(); ++i) { if(itemText(i) == s) { if(m_pVarNum != nullptr) *m_pVarNum = i; if(m_pVarStr != nullptr) *m_pVarStr = s; setCurrentIndex(i); return; } } } }; class OptionEncodingComboBox : public QComboBox, public OptionItem { Q_OBJECT QVector m_codecVec; QTextCodec** m_ppVarCodec; public: OptionEncodingComboBox(const QString& saveName, QTextCodec** ppVarCodec, QWidget* pParent, OptionDialog* pOD) : QComboBox(pParent), OptionItem(pOD, saveName) { m_ppVarCodec = ppVarCodec; insertCodec(i18n("Unicode, 8 bit"), QTextCodec::codecForName("UTF-8")); insertCodec(i18n("Unicode"), QTextCodec::codecForName("iso-10646-UCS-2")); insertCodec(i18n("Latin1"), QTextCodec::codecForName("iso 8859-1")); // First sort codec names: std::map names; QList mibs = QTextCodec::availableMibs(); foreach(int i, mibs) { QTextCodec* c = QTextCodec::codecForMib(i); if(c != nullptr) names[QString(c->name()).toUpper()] = c; } std::map::iterator it; for(it = names.begin(); it != names.end(); ++it) { insertCodec("", it->second); } this->setToolTip(i18n( "Change this if non-ASCII characters are not displayed correctly.")); } void insertCodec(const QString& visibleCodecName, QTextCodec* c) { if(c != nullptr) { for(int i = 0; i < m_codecVec.size(); ++i) { if(c == m_codecVec[i]) return; // don't insert any codec twice } addItem(visibleCodecName.isEmpty() ? QString(c->name()) : visibleCodecName + " (" + c->name() + ")", (int)m_codecVec.size()); m_codecVec.push_back(c); } } void setToDefault() override { QString defaultName = QTextCodec::codecForLocale()->name(); for(int i = 0; i < count(); ++i) { if(defaultName == itemText(i) && m_codecVec[i] == QTextCodec::codecForLocale()) { setCurrentIndex(i); if(m_ppVarCodec != nullptr) { *m_ppVarCodec = m_codecVec[i]; } return; } } setCurrentIndex(0); if(m_ppVarCodec != nullptr) { *m_ppVarCodec = m_codecVec[0]; } } void setToCurrent() override { if(m_ppVarCodec != nullptr) { for(int i = 0; i < m_codecVec.size(); ++i) { if(*m_ppVarCodec == m_codecVec[i]) { setCurrentIndex(i); break; } } } } void apply() override { if(m_ppVarCodec != nullptr) { *m_ppVarCodec = m_codecVec[currentIndex()]; } } void write(ValueMap* config) override { if(m_ppVarCodec != nullptr) config->writeEntry(m_saveName, QString((*m_ppVarCodec)->name())); } void read(ValueMap* config) override { QString codecName = config->readEntry(m_saveName, QString(m_codecVec[currentIndex()]->name())); for(int i = 0; i < m_codecVec.size(); ++i) { if(codecName == m_codecVec[i]->name()) { setCurrentIndex(i); if(m_ppVarCodec != nullptr) *m_ppVarCodec = m_codecVec[i]; break; } } } protected: void preserve() override { m_preservedVal = currentIndex(); } void unpreserve() override { setCurrentIndex(m_preservedVal); } int m_preservedVal; }; OptionDialog::OptionDialog(bool bShowDirMergeSettings, QWidget* parent, char* name) : // KPageDialog( IconList, i18n("Configure"), Help|Default|Apply|Ok|Cancel, // Ok, parent, name, true /*modal*/, true ) KPageDialog(parent) { setFaceType(List); setWindowTitle(i18n("Configure")); setStandardButtons(QDialogButtonBox::Help | QDialogButtonBox::RestoreDefaults | QDialogButtonBox::Apply | QDialogButtonBox::Ok | QDialogButtonBox::Cancel); // TODO KF5 necessary? setDefaultButton( Ok ); setObjectName(name); setModal(true); //showButtonSeparator( true ); //setHelp( "kdiff3/index.html", QString::null ); setupFontPage(); setupColorPage(); setupEditPage(); setupDiffPage(); setupMergePage(); setupOtherOptions(); if(bShowDirMergeSettings) setupDirectoryMergePage(); setupRegionalPage(); setupIntegrationPage(); //setupKeysPage(); // Initialize all values in the dialog resetToDefaults(); slotApply(); connect(button(QDialogButtonBox::Apply), &QPushButton::clicked, this, &OptionDialog::slotApply); connect(button(QDialogButtonBox::Ok), &QPushButton::clicked, this, &OptionDialog::slotOk); connect(button(QDialogButtonBox::RestoreDefaults), &QPushButton::clicked, this, &OptionDialog::slotDefault); connect(button(QDialogButtonBox::Cancel), &QPushButton::clicked, this, &QDialog::reject); connect(button(QDialogButtonBox::Help), &QPushButton::clicked, this, &OptionDialog::helpRequested); //connect(this, &OptionDialog::applyClicked, this, &OptionDialog::slotApply); //helpClicked() is connected in KDiff3App::KDiff3App -- Really where? //connect(this, &OptionDialog::defaultClicked, this, &OptionDialog::slotDefault); } void OptionDialog::helpRequested() { KHelpClient::invokeHelp(QStringLiteral("kdiff3/index.html"), QString()); } OptionDialog::~OptionDialog(void) { } void OptionDialog::setupOtherOptions() { //TODO move to Options class new OptionToggleAction(false, "AutoAdvance", &m_options.m_bAutoAdvance, this); new OptionToggleAction(true, "ShowWhiteSpaceCharacters", &m_options.m_bShowWhiteSpaceCharacters, this); new OptionToggleAction(true, "ShowWhiteSpace", &m_options.m_bShowWhiteSpace, this); new OptionToggleAction(false, "ShowLineNumbers", &m_options.m_bShowLineNumbers, this); new OptionToggleAction(true, "HorizDiffWindowSplitting", &m_options.m_bHorizDiffWindowSplitting, this); new OptionToggleAction(false, "WordWrap", &m_options.m_bWordWrap, this); new OptionToggleAction(true, "ShowIdenticalFiles", &m_options.m_bDmShowIdenticalFiles, this); new OptionToggleAction(true, "Show Toolbar", &m_options.m_bShowToolBar, this); new OptionToggleAction(true, "Show Statusbar", &m_options.m_bShowStatusBar, this); /* TODO manage toolbar positioning */ new OptionNum( Qt::TopToolBarArea, "ToolBarPos", (int*)&m_options.m_toolBarPos, this ); new OptionSize(QSize(600, 400), "Geometry", &m_options.m_geometry, this); new OptionPoint(QPoint(0, 22), "Position", &m_options.m_position, this); new OptionToggleAction(false, "WindowStateMaximised", &m_options.m_bMaximised, this); new OptionStringList("RecentAFiles", &m_options.m_recentAFiles, this); new OptionStringList("RecentBFiles", &m_options.m_recentBFiles, this); new OptionStringList("RecentCFiles", &m_options.m_recentCFiles, this); new OptionStringList("RecentOutputFiles", &m_options.m_recentOutputFiles, this); new OptionStringList("RecentEncodings", &m_options.m_recentEncodings, this); } void OptionDialog::setupFontPage(void) { QFrame* page = new QFrame(); KPageWidgetItem* pageItem = new KPageWidgetItem(page, i18n("Font")); pageItem->setHeader(i18n("Editor & Diff Output Font")); //not all themes have this icon if(QIcon::hasThemeIcon(QStringLiteral("font-select-symbolic"))) pageItem->setIcon(QIcon::fromTheme(QStringLiteral("font-select-symbolic"))); else pageItem->setIcon(QIcon::fromTheme(QStringLiteral("preferences-desktop-font"))); addPage(pageItem); QVBoxLayout* topLayout = new QVBoxLayout(page); topLayout->setMargin(5); //requires QT 5.2 or later. static const QFont defaultFont = QFontDatabase::systemFont(QFontDatabase::FixedFont); ; static QFont defaultAppFont = QApplication::font(); OptionFontChooser* pAppFontChooser = new OptionFontChooser(defaultAppFont, "ApplicationFont", &m_options.m_appFont, page, this); topLayout->addWidget(pAppFontChooser); pAppFontChooser->setTitle(i18n("Application font")); OptionFontChooser* pFontChooser = new OptionFontChooser(defaultFont, "Font", &m_options.m_font, page, this); topLayout->addWidget(pFontChooser); pFontChooser->setTitle(i18n("File view font")); QGridLayout* gbox = new QGridLayout(); topLayout->addLayout(gbox); //int line=0; // This currently does not work (see rendering in class DiffTextWindow) //OptionCheckBox* pItalicDeltas = new OptionCheckBox( i18n("Italic font for deltas"), false, "ItalicForDeltas", &m_options.m_bItalicForDeltas, page, this ); //gbox->addWidget( pItalicDeltas, line, 0, 1, 2 ); //pItalicDeltas->setToolTip( i18n( // "Selects the italic version of the font for differences.\n" // "If the font doesn't support italic characters, then this does nothing.") // ); } void OptionDialog::setupColorPage(void) { QFrame* page = new QFrame(); KPageWidgetItem* pageItem = new KPageWidgetItem(page, i18n("Color")); pageItem->setHeader(i18n("Colors Settings")); pageItem->setIcon(QIcon::fromTheme(QStringLiteral("colormanagement"))); addPage(pageItem); QVBoxLayout* topLayout = new QVBoxLayout(page); topLayout->setMargin(5); QGridLayout* gbox = new QGridLayout(); gbox->setColumnStretch(1, 5); topLayout->addLayout(gbox); QLabel* label; int line = 0; int depth = QPixmap::defaultDepth(); bool bLowColor = depth <= 8; label = new QLabel(i18n("Editor and Diff Views:"), page); gbox->addWidget(label, line, 0); QFont f(label->font()); f.setBold(true); label->setFont(f); ++line; OptionColorButton* pFgColor = new OptionColorButton(Qt::black, "FgColor", &m_options.m_fgColor, page, this); label = new QLabel(i18n("Foreground color:"), page); label->setBuddy(pFgColor); gbox->addWidget(label, line, 0); gbox->addWidget(pFgColor, line, 1); ++line; OptionColorButton* pBgColor = new OptionColorButton(Qt::white, "BgColor", &m_options.m_bgColor, page, this); label = new QLabel(i18n("Background color:"), page); label->setBuddy(pBgColor); gbox->addWidget(label, line, 0); gbox->addWidget(pBgColor, line, 1); ++line; OptionColorButton* pDiffBgColor = new OptionColorButton( bLowColor ? QColor(Qt::lightGray) : qRgb(224, 224, 224), "DiffBgColor", &m_options.m_diffBgColor, page, this); label = new QLabel(i18n("Diff background color:"), page); label->setBuddy(pDiffBgColor); gbox->addWidget(label, line, 0); gbox->addWidget(pDiffBgColor, line, 1); ++line; OptionColorButton* pColorA = new OptionColorButton( bLowColor ? qRgb(0, 0, 255) : qRgb(0, 0, 200) /*blue*/, "ColorA", &m_options.m_colorA, page, this); label = new QLabel(i18n("Color A:"), page); label->setBuddy(pColorA); gbox->addWidget(label, line, 0); gbox->addWidget(pColorA, line, 1); ++line; OptionColorButton* pColorB = new OptionColorButton( bLowColor ? qRgb(0, 128, 0) : qRgb(0, 150, 0) /*green*/, "ColorB", &m_options.m_colorB, page, this); label = new QLabel(i18n("Color B:"), page); label->setBuddy(pColorB); gbox->addWidget(label, line, 0); gbox->addWidget(pColorB, line, 1); ++line; OptionColorButton* pColorC = new OptionColorButton( bLowColor ? qRgb(128, 0, 128) : qRgb(150, 0, 150) /*magenta*/, "ColorC", &m_options.m_colorC, page, this); label = new QLabel(i18n("Color C:"), page); label->setBuddy(pColorC); gbox->addWidget(label, line, 0); gbox->addWidget(pColorC, line, 1); ++line; OptionColorButton* pColorForConflict = new OptionColorButton(Qt::red, "ColorForConflict", &m_options.m_colorForConflict, page, this); label = new QLabel(i18n("Conflict color:"), page); label->setBuddy(pColorForConflict); gbox->addWidget(label, line, 0); gbox->addWidget(pColorForConflict, line, 1); ++line; OptionColorButton* pColor = new OptionColorButton( bLowColor ? qRgb(192, 192, 192) : qRgb(220, 220, 100), "CurrentRangeBgColor", &m_options.m_currentRangeBgColor, page, this); label = new QLabel(i18n("Current range background color:"), page); label->setBuddy(pColor); gbox->addWidget(label, line, 0); gbox->addWidget(pColor, line, 1); ++line; pColor = new OptionColorButton( bLowColor ? qRgb(255, 255, 0) : qRgb(255, 255, 150), "CurrentRangeDiffBgColor", &m_options.m_currentRangeDiffBgColor, page, this); label = new QLabel(i18n("Current range diff background color:"), page); label->setBuddy(pColor); gbox->addWidget(label, line, 0); gbox->addWidget(pColor, line, 1); ++line; pColor = new OptionColorButton(qRgb(0xff, 0xd0, 0x80), "ManualAlignmentRangeColor", &m_options.m_manualHelpRangeColor, page, this); label = new QLabel(i18n("Color for manually aligned difference ranges:"), page); label->setBuddy(pColor); gbox->addWidget(label, line, 0); gbox->addWidget(pColor, line, 1); ++line; label = new QLabel(i18n("Directory Comparison View:"), page); gbox->addWidget(label, line, 0); label->setFont(f); ++line; pColor = new OptionColorButton(qRgb(0, 0xd0, 0), "NewestFileColor", &m_options.m_newestFileColor, page, this); label = new QLabel(i18n("Newest file color:"), page); label->setBuddy(pColor); gbox->addWidget(label, line, 0); gbox->addWidget(pColor, line, 1); QString dirColorTip = i18n("Changing this color will only be effective when starting the next directory comparison."); label->setToolTip(dirColorTip); ++line; pColor = new OptionColorButton(qRgb(0xf0, 0, 0), "OldestFileColor", &m_options.m_oldestFileColor, page, this); label = new QLabel(i18n("Oldest file color:"), page); label->setBuddy(pColor); gbox->addWidget(label, line, 0); gbox->addWidget(pColor, line, 1); label->setToolTip(dirColorTip); ++line; pColor = new OptionColorButton(qRgb(0xc0, 0xc0, 0), "MidAgeFileColor", &m_options.m_midAgeFileColor, page, this); label = new QLabel(i18n("Middle age file color:"), page); label->setBuddy(pColor); gbox->addWidget(label, line, 0); gbox->addWidget(pColor, line, 1); label->setToolTip(dirColorTip); ++line; pColor = new OptionColorButton(qRgb(0, 0, 0), "MissingFileColor", &m_options.m_missingFileColor, page, this); label = new QLabel(i18n("Color for missing files:"), page); label->setBuddy(pColor); gbox->addWidget(label, line, 0); gbox->addWidget(pColor, line, 1); label->setToolTip(dirColorTip); ++line; topLayout->addStretch(10); } void OptionDialog::setupEditPage(void) { QFrame* page = new QFrame(); KPageWidgetItem* pageItem = new KPageWidgetItem(page, i18n("Editor")); pageItem->setHeader(i18n("Editor Behavior")); pageItem->setIcon(QIcon::fromTheme(QStringLiteral("accessories-text-editor"))); addPage(pageItem); QVBoxLayout* topLayout = new QVBoxLayout(page); topLayout->setMargin(5); QGridLayout* gbox = new QGridLayout(); gbox->setColumnStretch(1, 5); topLayout->addLayout(gbox); QLabel* label; int line = 0; OptionCheckBox* pReplaceTabs = new OptionCheckBox(i18n("Tab inserts spaces"), false, "ReplaceTabs", &m_options.m_bReplaceTabs, page, this); gbox->addWidget(pReplaceTabs, line, 0, 1, 2); pReplaceTabs->setToolTip(i18n( "On: Pressing tab generates the appropriate number of spaces.\n" "Off: A tab character will be inserted.")); ++line; OptionIntEdit* pTabSize = new OptionIntEdit(8, "TabSize", &m_options.m_tabSize, 1, 100, page, this); label = new QLabel(i18n("Tab size:"), page); label->setBuddy(pTabSize); gbox->addWidget(label, line, 0); gbox->addWidget(pTabSize, line, 1); ++line; OptionCheckBox* pAutoIndentation = new OptionCheckBox(i18n("Auto indentation"), true, "AutoIndentation", &m_options.m_bAutoIndentation, page, this); gbox->addWidget(pAutoIndentation, line, 0, 1, 2); pAutoIndentation->setToolTip(i18n( "On: The indentation of the previous line is used for a new line.\n")); ++line; OptionCheckBox* pAutoCopySelection = new OptionCheckBox(i18n("Auto copy selection"), false, "AutoCopySelection", &m_options.m_bAutoCopySelection, page, this); gbox->addWidget(pAutoCopySelection, line, 0, 1, 2); pAutoCopySelection->setToolTip(i18n( "On: Any selection is immediately written to the clipboard.\n" "Off: You must explicitly copy e.g. via Ctrl-C.")); ++line; label = new QLabel(i18n("Line end style:"), page); gbox->addWidget(label, line, 0); OptionComboBox* pLineEndStyle = new OptionComboBox(eLineEndStyleAutoDetect, "LineEndStyle", &m_options.m_lineEndStyle, page, this); gbox->addWidget(pLineEndStyle, line, 1); pLineEndStyle->insertItem(eLineEndStyleUnix, "Unix"); pLineEndStyle->insertItem(eLineEndStyleDos, "Dos/Windows"); pLineEndStyle->insertItem(eLineEndStyleAutoDetect, "Autodetect"); label->setToolTip(i18n( "Sets the line endings for when an edited file is saved.\n" "DOS/Windows: CR+LF; UNIX: LF; with CR=0D, LF=0A")); ++line; topLayout->addStretch(10); } void OptionDialog::setupDiffPage(void) { QFrame* page = new QFrame(); KPageWidgetItem* pageItem = new KPageWidgetItem(page, i18n("Diff")); pageItem->setHeader(i18n("Diff Settings")); pageItem->setIcon(QIcon::fromTheme(QStringLiteral("text-x-patch"))); addPage(pageItem); QVBoxLayout* topLayout = new QVBoxLayout(page); topLayout->setMargin(5); QGridLayout* gbox = new QGridLayout(); gbox->setColumnStretch(1, 5); topLayout->addLayout(gbox); int line = 0; QLabel* label = nullptr; m_options.m_bPreserveCarriageReturn = false; /* OptionCheckBox* pPreserveCarriageReturn = new OptionCheckBox( i18n("Preserve carriage return"), false, "PreserveCarriageReturn", &m_options.m_bPreserveCarriageReturn, page, this ); gbox->addWidget( pPreserveCarriageReturn, line, 0, 1, 2 ); pPreserveCarriageReturn->setToolTip( i18n( "Show carriage return characters '\\r' if they exist.\n" "Helps to compare files that were modified under different operating systems.") ); ++line; */ OptionCheckBox* pIgnoreNumbers = new OptionCheckBox(i18n("Ignore numbers (treat as white space)"), false, "IgnoreNumbers", &m_options.m_bIgnoreNumbers, page, this); gbox->addWidget(pIgnoreNumbers, line, 0, 1, 2); pIgnoreNumbers->setToolTip(i18n( "Ignore number characters during line matching phase. (Similar to Ignore white space.)\n" "Might help to compare files with numeric data.")); ++line; OptionCheckBox* pIgnoreComments = new OptionCheckBox(i18n("Ignore C/C++ comments (treat as white space)"), false, "IgnoreComments", &m_options.m_bIgnoreComments, page, this); gbox->addWidget(pIgnoreComments, line, 0, 1, 2); pIgnoreComments->setToolTip(i18n("Treat C/C++ comments like white space.")); ++line; OptionCheckBox* pIgnoreCase = new OptionCheckBox(i18n("Ignore case (treat as white space)"), false, "IgnoreCase", &m_options.m_bIgnoreCase, page, this); gbox->addWidget(pIgnoreCase, line, 0, 1, 2); pIgnoreCase->setToolTip(i18n( "Treat case differences like white space changes. ('a'<=>'A')")); ++line; label = new QLabel(i18n("Preprocessor command:"), page); gbox->addWidget(label, line, 0); OptionLineEdit* pLE = new OptionLineEdit("", "PreProcessorCmd", &m_options.m_PreProcessorCmd, page, this); gbox->addWidget(pLE, line, 1); label->setToolTip(i18n("User defined pre-processing. (See the docs for details.)")); ++line; label = new QLabel(i18n("Line-matching preprocessor command:"), page); gbox->addWidget(label, line, 0); pLE = new OptionLineEdit("", "LineMatchingPreProcessorCmd", &m_options.m_LineMatchingPreProcessorCmd, page, this); gbox->addWidget(pLE, line, 1); label->setToolTip(i18n("This pre-processor is only used during line matching.\n(See the docs for details.)")); ++line; OptionCheckBox* pTryHard = new OptionCheckBox(i18n("Try hard (slower)"), true, "TryHard", &m_options.m_bTryHard, page, this); gbox->addWidget(pTryHard, line, 0, 1, 2); pTryHard->setToolTip(i18n( "Enables the --minimal option for the external diff.\n" "The analysis of big files will be much slower.")); ++line; OptionCheckBox* pDiff3AlignBC = new OptionCheckBox(i18n("Align B and C for 3 input files"), false, "Diff3AlignBC", &m_options.m_bDiff3AlignBC, page, this); gbox->addWidget(pDiff3AlignBC, line, 0, 1, 2); pDiff3AlignBC->setToolTip(i18n( "Try to align B and C when comparing or merging three input files.\n" "Not recommended for merging because merge might get more complicated.\n" "(Default is off.)")); ++line; topLayout->addStretch(10); } void OptionDialog::setupMergePage(void) { QFrame* page = new QFrame(); KPageWidgetItem* pageItem = new KPageWidgetItem(page, i18n("Merge")); pageItem->setHeader(i18n("Merge Settings")); pageItem->setIcon(QIcon::fromTheme(QStringLiteral("merge"))); addPage(pageItem); QVBoxLayout* topLayout = new QVBoxLayout(page); topLayout->setMargin(5); QGridLayout* gbox = new QGridLayout(); gbox->setColumnStretch(1, 5); topLayout->addLayout(gbox); int line = 0; QLabel* label = nullptr; label = new QLabel(i18n("Auto advance delay (ms):"), page); gbox->addWidget(label, line, 0); OptionIntEdit* pAutoAdvanceDelay = new OptionIntEdit(500, "AutoAdvanceDelay", &m_options.m_autoAdvanceDelay, 0, 2000, page, this); gbox->addWidget(pAutoAdvanceDelay, line, 1); label->setToolTip(i18n( "When in Auto-Advance mode the result of the current selection is shown \n" "for the specified time, before jumping to the next conflict. Range: 0-2000 ms")); ++line; OptionCheckBox* pShowInfoDialogs = new OptionCheckBox(i18n("Show info dialogs"), true, "ShowInfoDialogs", &m_options.m_bShowInfoDialogs, page, this); gbox->addWidget(pShowInfoDialogs, line, 0, 1, 2); pShowInfoDialogs->setToolTip(i18n("Show a dialog with information about the number of conflicts.")); ++line; label = new QLabel(i18n("White space 2-file merge default:"), page); gbox->addWidget(label, line, 0); OptionComboBox* pWhiteSpace2FileMergeDefault = new OptionComboBox(0, "WhiteSpace2FileMergeDefault", &m_options.m_whiteSpace2FileMergeDefault, page, this); gbox->addWidget(pWhiteSpace2FileMergeDefault, line, 1); pWhiteSpace2FileMergeDefault->insertItem(0, i18n("Manual Choice")); pWhiteSpace2FileMergeDefault->insertItem(1, "A"); pWhiteSpace2FileMergeDefault->insertItem(2, "B"); label->setToolTip(i18n( "Allow the merge algorithm to automatically select an input for " "white-space-only changes.")); ++line; label = new QLabel(i18n("White space 3-file merge default:"), page); gbox->addWidget(label, line, 0); OptionComboBox* pWhiteSpace3FileMergeDefault = new OptionComboBox(0, "WhiteSpace3FileMergeDefault", &m_options.m_whiteSpace3FileMergeDefault, page, this); gbox->addWidget(pWhiteSpace3FileMergeDefault, line, 1); pWhiteSpace3FileMergeDefault->insertItem(0, i18n("Manual Choice")); pWhiteSpace3FileMergeDefault->insertItem(1, "A"); pWhiteSpace3FileMergeDefault->insertItem(2, "B"); pWhiteSpace3FileMergeDefault->insertItem(3, "C"); label->setToolTip(i18n( "Allow the merge algorithm to automatically select an input for " "white-space-only changes.")); ++line; QGroupBox* pGroupBox = new QGroupBox(i18n("Automatic Merge Regular Expression")); gbox->addWidget(pGroupBox, line, 0, 1, 2); ++line; { QGridLayout* gbox = new QGridLayout(pGroupBox); gbox->setColumnStretch(1, 10); int line = 0; label = new QLabel(i18n("Auto merge regular expression:"), page); gbox->addWidget(label, line, 0); m_pAutoMergeRegExpLineEdit = new OptionLineEdit(".*\\$(Version|Header|Date|Author).*\\$.*", "AutoMergeRegExp", &m_options.m_autoMergeRegExp, page, this); gbox->addWidget(m_pAutoMergeRegExpLineEdit, line, 1); s_autoMergeRegExpToolTip = i18n("Regular expression for lines where KDiff3 should automatically choose one source.\n" "When a line with a conflict matches the regular expression then\n" "- if available - C, otherwise B will be chosen."); label->setToolTip(s_autoMergeRegExpToolTip); ++line; OptionCheckBox* pAutoMergeRegExp = new OptionCheckBox(i18n("Run regular expression auto merge on merge start"), false, "RunRegExpAutoMergeOnMergeStart", &m_options.m_bRunRegExpAutoMergeOnMergeStart, page, this); gbox->addWidget(pAutoMergeRegExp, line, 0, 1, 2); pAutoMergeRegExp->setToolTip(i18n("Run the merge for auto merge regular expressions\n" "immediately when a merge starts.\n")); ++line; } pGroupBox = new QGroupBox(i18n("Version Control History Merging")); gbox->addWidget(pGroupBox, line, 0, 1, 2); ++line; { QGridLayout* gbox = new QGridLayout(pGroupBox); gbox->setColumnStretch(1, 10); int line = 0; label = new QLabel(i18n("History start regular expression:"), page); gbox->addWidget(label, line, 0); m_pHistoryStartRegExpLineEdit = new OptionLineEdit(".*\\$Log.*\\$.*", "HistoryStartRegExp", &m_options.m_historyStartRegExp, page, this); gbox->addWidget(m_pHistoryStartRegExpLineEdit, line, 1); s_historyStartRegExpToolTip = i18n("Regular expression for the start of the version control history entry.\n" "Usually this line contains the \"$Log$\" keyword.\n" "Default value: \".*\\$Log.*\\$.*\""); label->setToolTip(s_historyStartRegExpToolTip); ++line; label = new QLabel(i18n("History entry start regular expression:"), page); gbox->addWidget(label, line, 0); // Example line: "** \main\rolle_fsp_dev_008\1 17 Aug 2001 10:45:44 rolle" QString historyEntryStartDefault = "\\s*\\\\main\\\\(\\S+)\\s+" // Start with "\main\" "([0-9]+) " // day "(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) " //month "([0-9][0-9][0-9][0-9]) " // year "([0-9][0-9]:[0-9][0-9]:[0-9][0-9])\\s+(.*)"; // time, name m_pHistoryEntryStartRegExpLineEdit = new OptionLineEdit(historyEntryStartDefault, "HistoryEntryStartRegExp", &m_options.m_historyEntryStartRegExp, page, this); gbox->addWidget(m_pHistoryEntryStartRegExpLineEdit, line, 1); s_historyEntryStartRegExpToolTip = i18n("A version control history entry consists of several lines.\n" "Specify the regular expression to detect the first line (without the leading comment).\n" "Use parentheses to group the keys you want to use for sorting.\n" "If left empty, then KDiff3 assumes that empty lines separate history entries.\n" "See the documentation for details."); label->setToolTip(s_historyEntryStartRegExpToolTip); ++line; m_pHistoryMergeSorting = new OptionCheckBox(i18n("History merge sorting"), false, "HistoryMergeSorting", &m_options.m_bHistoryMergeSorting, page, this); gbox->addWidget(m_pHistoryMergeSorting, line, 0, 1, 2); m_pHistoryMergeSorting->setToolTip(i18n("Sort version control history by a key.")); ++line; //QString branch = newHistoryEntry.cap(1); //int day = newHistoryEntry.cap(2).toInt(); //int month = QString("Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec").find(newHistoryEntry.cap(3))/4 + 1; //int year = newHistoryEntry.cap(4).toInt(); //QString time = newHistoryEntry.cap(5); //QString name = newHistoryEntry.cap(6); QString defaultSortKeyOrder = "4,3,2,5,1,6"; //QDate(year,month,day).toString(Qt::ISODate) +" "+ time + " " + branch + " " + name; label = new QLabel(i18n("History entry start sort key order:"), page); gbox->addWidget(label, line, 0); m_pHistorySortKeyOrderLineEdit = new OptionLineEdit(defaultSortKeyOrder, "HistoryEntryStartSortKeyOrder", &m_options.m_historyEntryStartSortKeyOrder, page, this); gbox->addWidget(m_pHistorySortKeyOrderLineEdit, line, 1); s_historyEntryStartSortKeyOrderToolTip = i18n("Each pair of parentheses used in the regular expression for the history start entry\n" "groups a key that can be used for sorting.\n" "Specify the list of keys (that are numbered in order of occurrence\n" "starting with 1) using ',' as separator (e.g. \"4,5,6,1,2,3,7\").\n" "If left empty, then no sorting will be done.\n" "See the documentation for details."); label->setToolTip(s_historyEntryStartSortKeyOrderToolTip); m_pHistorySortKeyOrderLineEdit->setEnabled(false); connect(m_pHistoryMergeSorting, &OptionCheckBox::toggled, m_pHistorySortKeyOrderLineEdit, &OptionLineEdit::setEnabled); ++line; m_pHistoryAutoMerge = new OptionCheckBox(i18n("Merge version control history on merge start"), false, "RunHistoryAutoMergeOnMergeStart", &m_options.m_bRunHistoryAutoMergeOnMergeStart, page, this); gbox->addWidget(m_pHistoryAutoMerge, line, 0, 1, 2); m_pHistoryAutoMerge->setToolTip(i18n("Run version control history automerge on merge start.")); ++line; OptionIntEdit* pMaxNofHistoryEntries = new OptionIntEdit(-1, "MaxNofHistoryEntries", &m_options.m_maxNofHistoryEntries, -1, 1000, page, this); label = new QLabel(i18n("Max number of history entries:"), page); gbox->addWidget(label, line, 0); gbox->addWidget(pMaxNofHistoryEntries, line, 1); pMaxNofHistoryEntries->setToolTip(i18n("Cut off after specified number. Use -1 for infinite number of entries.")); ++line; } QPushButton* pButton = new QPushButton(i18n("Test your regular expressions"), page); gbox->addWidget(pButton, line, 0); connect(pButton, &QPushButton::clicked, this, &OptionDialog::slotHistoryMergeRegExpTester); ++line; label = new QLabel(i18n("Irrelevant merge command:"), page); gbox->addWidget(label, line, 0); OptionLineEdit* pLE = new OptionLineEdit("", "IrrelevantMergeCmd", &m_options.m_IrrelevantMergeCmd, page, this); gbox->addWidget(pLE, line, 1); label->setToolTip(i18n("If specified this script is run after automerge\n" "when no other relevant changes were detected.\n" "Called with the parameters: filename1 filename2 filename3")); ++line; OptionCheckBox* pAutoSaveAndQuit = new OptionCheckBox(i18n("Auto save and quit on merge without conflicts"), false, "AutoSaveAndQuitOnMergeWithoutConflicts", &m_options.m_bAutoSaveAndQuitOnMergeWithoutConflicts, page, this); gbox->addWidget(pAutoSaveAndQuit, line, 0, 1, 2); pAutoSaveAndQuit->setToolTip(i18n("If KDiff3 was started for a file-merge from the command line and all\n" "conflicts are solvable without user interaction then automatically save and quit.\n" "(Similar to command line option \"--auto\".)")); ++line; topLayout->addStretch(10); } void OptionDialog::setupDirectoryMergePage(void) { QFrame* page = new QFrame(); KPageWidgetItem* pageItem = new KPageWidgetItem(page, i18n("Directory")); pageItem->setHeader(i18n("Directory")); pageItem->setIcon(QIcon::fromTheme(QStringLiteral("inode-directory"))); addPage(pageItem); QVBoxLayout* topLayout = new QVBoxLayout(page); topLayout->setMargin(5); QGridLayout* gbox = new QGridLayout(); gbox->setColumnStretch(1, 5); topLayout->addLayout(gbox); int line = 0; OptionCheckBox* pRecursiveDirs = new OptionCheckBox(i18n("Recursive directories"), true, "RecursiveDirs", &m_options.m_bDmRecursiveDirs, page, this); gbox->addWidget(pRecursiveDirs, line, 0, 1, 2); pRecursiveDirs->setToolTip(i18n("Whether to analyze subdirectories or not.")); ++line; QLabel* label = new QLabel(i18n("File pattern(s):"), page); gbox->addWidget(label, line, 0); OptionLineEdit* pFilePattern = new OptionLineEdit("*", "FilePattern", &m_options.m_DmFilePattern, page, this); gbox->addWidget(pFilePattern, line, 1); label->setToolTip(i18n( "Pattern(s) of files to be analyzed. \n" "Wildcards: '*' and '?'\n" "Several Patterns can be specified by using the separator: ';'")); ++line; label = new QLabel(i18n("File-anti-pattern(s):"), page); gbox->addWidget(label, line, 0); OptionLineEdit* pFileAntiPattern = new OptionLineEdit("*.orig;*.o;*.obj;*.rej;*.bak", "FileAntiPattern", &m_options.m_DmFileAntiPattern, page, this); gbox->addWidget(pFileAntiPattern, line, 1); label->setToolTip(i18n( "Pattern(s) of files to be excluded from analysis. \n" "Wildcards: '*' and '?'\n" "Several Patterns can be specified by using the separator: ';'")); ++line; label = new QLabel(i18n("Dir-anti-pattern(s):"), page); gbox->addWidget(label, line, 0); OptionLineEdit* pDirAntiPattern = new OptionLineEdit("CVS;.deps;.svn;.hg;.git", "DirAntiPattern", &m_options.m_DmDirAntiPattern, page, this); gbox->addWidget(pDirAntiPattern, line, 1); label->setToolTip(i18n( "Pattern(s) of directories to be excluded from analysis. \n" "Wildcards: '*' and '?'\n" "Several Patterns can be specified by using the separator: ';'")); ++line; OptionCheckBox* pUseCvsIgnore = new OptionCheckBox(i18n("Use .cvsignore"), false, "UseCvsIgnore", &m_options.m_bDmUseCvsIgnore, page, this); gbox->addWidget(pUseCvsIgnore, line, 0, 1, 2); pUseCvsIgnore->setToolTip(i18n( "Extends the antipattern to anything that would be ignored by CVS.\n" "Via local \".cvsignore\" files this can be directory specific.")); ++line; OptionCheckBox* pFindHidden = new OptionCheckBox(i18n("Find hidden files and directories"), true, "FindHidden", &m_options.m_bDmFindHidden, page, this); gbox->addWidget(pFindHidden, line, 0, 1, 2); #if defined(Q_OS_WIN) pFindHidden->setToolTip(i18n("Finds files and directories with the hidden attribute.")); #else pFindHidden->setToolTip(i18n("Finds files and directories starting with '.'.")); #endif ++line; OptionCheckBox* pFollowFileLinks = new OptionCheckBox(i18n("Follow file links"), false, "FollowFileLinks", &m_options.m_bDmFollowFileLinks, page, this); gbox->addWidget(pFollowFileLinks, line, 0, 1, 2); pFollowFileLinks->setToolTip(i18n( "On: Compare the file the link points to.\n" "Off: Compare the links.")); ++line; OptionCheckBox* pFollowDirLinks = new OptionCheckBox(i18n("Follow directory links"), false, "FollowDirLinks", &m_options.m_bDmFollowDirLinks, page, this); gbox->addWidget(pFollowDirLinks, line, 0, 1, 2); pFollowDirLinks->setToolTip(i18n( "On: Compare the directory the link points to.\n" "Off: Compare the links.")); ++line; //OptionCheckBox* pShowOnlyDeltas = new OptionCheckBox( i18n("List only deltas"),false,"ListOnlyDeltas", &m_options.m_bDmShowOnlyDeltas, page, this ); //gbox->addWidget( pShowOnlyDeltas, line, 0, 1, 2 ); //pShowOnlyDeltas->setToolTip( i18n( // "Files and directories without change will not appear in the list.")); //++line; #if defined(Q_OS_WIN) bool bCaseSensitiveFilenameComparison = false; #else bool bCaseSensitiveFilenameComparison = true; #endif OptionCheckBox* pCaseSensitiveFileNames = new OptionCheckBox(i18n("Case sensitive filename comparison"), bCaseSensitiveFilenameComparison, "CaseSensitiveFilenameComparison", &m_options.m_bDmCaseSensitiveFilenameComparison, page, this); gbox->addWidget(pCaseSensitiveFileNames, line, 0, 1, 2); pCaseSensitiveFileNames->setToolTip(i18n( "The directory comparison will compare files or directories when their names match.\n" "Set this option if the case of the names must match. (Default for Windows is off, otherwise on.)")); ++line; OptionCheckBox* pUnfoldSubdirs = new OptionCheckBox(i18n("Unfold all subdirectories on load"), false, "UnfoldSubdirs", &m_options.m_bDmUnfoldSubdirs, page, this); gbox->addWidget(pUnfoldSubdirs, line, 0, 1, 2); pUnfoldSubdirs->setToolTip(i18n( "On: Unfold all subdirectories when starting a directory diff.\n" "Off: Leave subdirectories folded.")); ++line; OptionCheckBox* pSkipDirStatus = new OptionCheckBox(i18n("Skip directory status report"), false, "SkipDirStatus", &m_options.m_bDmSkipDirStatus, page, this); gbox->addWidget(pSkipDirStatus, line, 0, 1, 2); pSkipDirStatus->setToolTip(i18n( "On: Do not show the Directory Comparison Status.\n" "Off: Show the status dialog on start.")); ++line; QGroupBox* pBG = new QGroupBox(i18n("File Comparison Mode")); gbox->addWidget(pBG, line, 0, 1, 2); QVBoxLayout* pBGLayout = new QVBoxLayout(pBG); OptionRadioButton* pBinaryComparison = new OptionRadioButton(i18n("Binary comparison"), true, "BinaryComparison", &m_options.m_bDmBinaryComparison, pBG, this); pBinaryComparison->setToolTip(i18n("Binary comparison of each file. (Default)")); pBGLayout->addWidget(pBinaryComparison); OptionRadioButton* pFullAnalysis = new OptionRadioButton(i18n("Full analysis"), false, "FullAnalysis", &m_options.m_bDmFullAnalysis, pBG, this); pFullAnalysis->setToolTip(i18n("Do a full analysis and show statistics information in extra columns.\n" "(Slower than a binary comparison, much slower for binary files.)")); pBGLayout->addWidget(pFullAnalysis); OptionRadioButton* pTrustDate = new OptionRadioButton(i18n("Trust the size and modification date (unsafe)"), false, "TrustDate", &m_options.m_bDmTrustDate, pBG, this); pTrustDate->setToolTip(i18n("Assume that files are equal if the modification date and file length are equal.\n" "Files with equal contents but different modification dates will appear as different.\n" "Useful for big directories or slow networks.")); pBGLayout->addWidget(pTrustDate); OptionRadioButton* pTrustDateFallbackToBinary = new OptionRadioButton(i18n("Trust the size and date, but use binary comparison if date does not match (unsafe)"), false, "TrustDateFallbackToBinary", &m_options.m_bDmTrustDateFallbackToBinary, pBG, this); pTrustDateFallbackToBinary->setToolTip(i18n("Assume that files are equal if the modification date and file length are equal.\n" "If the dates are not equal but the sizes are, use binary comparison.\n" "Useful for big directories or slow networks.")); pBGLayout->addWidget(pTrustDateFallbackToBinary); OptionRadioButton* pTrustSize = new OptionRadioButton(i18n("Trust the size (unsafe)"), false, "TrustSize", &m_options.m_bDmTrustSize, pBG, this); pTrustSize->setToolTip(i18n("Assume that files are equal if their file lengths are equal.\n" "Useful for big directories or slow networks when the date is modified during download.")); pBGLayout->addWidget(pTrustSize); ++line; // Some two Dir-options: Affects only the default actions. OptionCheckBox* pSyncMode = new OptionCheckBox(i18n("Synchronize directories"), false, "SyncMode", &m_options.m_bDmSyncMode, page, this); gbox->addWidget(pSyncMode, line, 0, 1, 2); pSyncMode->setToolTip(i18n( "Offers to store files in both directories so that\n" "both directories are the same afterwards.\n" "Works only when comparing two directories without specifying a destination.")); ++line; // Allow white-space only differences to be considered equal OptionCheckBox* pWhiteSpaceDiffsEqual = new OptionCheckBox(i18n("White space differences considered equal"), true, "WhiteSpaceEqual", &m_options.m_bDmWhiteSpaceEqual, page, this); gbox->addWidget(pWhiteSpaceDiffsEqual, line, 0, 1, 2); pWhiteSpaceDiffsEqual->setToolTip(i18n( "If files differ only by white space consider them equal.\n" "This is only active when full analysis is chosen.")); connect(pFullAnalysis, &OptionRadioButton::toggled, pWhiteSpaceDiffsEqual, &OptionCheckBox::setEnabled); pWhiteSpaceDiffsEqual->setEnabled(false); ++line; OptionCheckBox* pCopyNewer = new OptionCheckBox(i18n("Copy newer instead of merging (unsafe)"), false, "CopyNewer", &m_options.m_bDmCopyNewer, page, this); gbox->addWidget(pCopyNewer, line, 0, 1, 2); pCopyNewer->setToolTip(i18n( "Do not look inside, just take the newer file.\n" "(Use this only if you know what you are doing!)\n" "Only effective when comparing two directories.")); ++line; OptionCheckBox* pCreateBakFiles = new OptionCheckBox(i18n("Backup files (.orig)"), true, "CreateBakFiles", &m_options.m_bDmCreateBakFiles, page, this); gbox->addWidget(pCreateBakFiles, line, 0, 1, 2); pCreateBakFiles->setToolTip(i18n( "If a file would be saved over an old file, then the old file\n" "will be renamed with a '.orig' extension instead of being deleted.")); ++line; topLayout->addStretch(10); } /* static void insertCodecs(OptionComboBox* p) { std::multimap m; // Using the multimap for case-insensitive sorting. int i; for(i=0;;++i) { QTextCodec* pCodec = QTextCodec::codecForIndex ( i ); if ( pCodec != 0 ) m.insert( std::make_pair( QString(pCodec->mimeName()).toUpper(), pCodec->mimeName()) ); else break; } p->insertItem( i18n("Auto"), 0 ); std::multimap::iterator mi; for(mi=m.begin(), i=0; mi!=m.end(); ++mi, ++i) p->insertItem(mi->second, i+1); } */ /* // UTF8-Codec that saves a BOM // UTF8-Codec that saves a BOM class Utf8BOMCodec : public QTextCodec { QTextCodec* m_pUtf8Codec; class PublicTextCodec : public QTextCodec { public: QString publicConvertToUnicode ( const char * p, int len, ConverterState* pState ) const { return convertToUnicode( p, len, pState ); } QByteArray publicConvertFromUnicode ( const QChar * input, int number, ConverterState * pState ) const { return convertFromUnicode( input, number, pState ); } }; public: Utf8BOMCodec() { m_pUtf8Codec = QTextCodec::codecForName("UTF-8"); } QByteArray name () const { return "UTF-8-BOM"; } int mibEnum () const { return 2123; } QByteArray convertFromUnicode ( const QChar * input, int number, ConverterState * pState ) const { QByteArray r; if ( pState && pState->state_data[2]==0) // state_data[2] not used by QUtf8::convertFromUnicode (see qutfcodec.cpp) { r += "\xEF\xBB\xBF"; pState->state_data[2]=1; pState->flags |= QTextCodec::IgnoreHeader; } r += ((PublicTextCodec*)m_pUtf8Codec)->publicConvertFromUnicode( input, number, pState ); return r; } QString convertToUnicode ( const char * p, int len, ConverterState* pState ) const { return ((PublicTextCodec*)m_pUtf8Codec)->publicConvertToUnicode( p, len, pState ); } }; */ void OptionDialog::setupRegionalPage(void) { /* TODO: What is this line supposed to do besides leak memmory? Intruduced as is in .91 no explaination new Utf8BOMCodec(); */ QFrame* page = new QFrame(); KPageWidgetItem* pageItem = new KPageWidgetItem(page, i18n("Regional Settings")); pageItem->setHeader(i18n("Regional Settings")); pageItem->setIcon(QIcon::fromTheme(QStringLiteral("preferences-desktop-locale"))); addPage(pageItem); QVBoxLayout* topLayout = new QVBoxLayout(page); topLayout->setMargin(5); QGridLayout* gbox = new QGridLayout(); gbox->setColumnStretch(1, 5); topLayout->addLayout(gbox); int line = 0; QLabel* label; m_pSameEncoding = new OptionCheckBox(i18n("Use the same encoding for everything:"), true, "SameEncoding", &m_options.m_bSameEncoding, page, this); gbox->addWidget(m_pSameEncoding, line, 0, 1, 2); m_pSameEncoding->setToolTip(i18n( "Enable this allows to change all encodings by changing the first only.\n" "Disable this if different individual settings are needed.")); ++line; label = new QLabel(i18n("Note: Local Encoding is \"%1\"", QLatin1String(QTextCodec::codecForLocale()->name())), page); gbox->addWidget(label, line, 0); ++line; label = new QLabel(i18n("File Encoding for A:"), page); gbox->addWidget(label, line, 0); m_pEncodingAComboBox = new OptionEncodingComboBox("EncodingForA", &m_options.m_pEncodingA, page, this); gbox->addWidget(m_pEncodingAComboBox, line, 1); QString autoDetectToolTip = i18n( "If enabled then Unicode (UTF-16 or UTF-8) encoding will be detected.\n" "If the file is not Unicode then the selected encoding will be used as fallback.\n" "(Unicode detection depends on the first bytes of a file.)"); m_pAutoDetectUnicodeA = new OptionCheckBox(i18n("Auto Detect Unicode"), true, "AutoDetectUnicodeA", &m_options.m_bAutoDetectUnicodeA, page, this); gbox->addWidget(m_pAutoDetectUnicodeA, line, 2); m_pAutoDetectUnicodeA->setToolTip(autoDetectToolTip); ++line; label = new QLabel(i18n("File Encoding for B:"), page); gbox->addWidget(label, line, 0); m_pEncodingBComboBox = new OptionEncodingComboBox("EncodingForB", &m_options.m_pEncodingB, page, this); gbox->addWidget(m_pEncodingBComboBox, line, 1); m_pAutoDetectUnicodeB = new OptionCheckBox(i18n("Auto Detect Unicode"), true, "AutoDetectUnicodeB", &m_options.m_bAutoDetectUnicodeB, page, this); gbox->addWidget(m_pAutoDetectUnicodeB, line, 2); m_pAutoDetectUnicodeB->setToolTip(autoDetectToolTip); ++line; label = new QLabel(i18n("File Encoding for C:"), page); gbox->addWidget(label, line, 0); m_pEncodingCComboBox = new OptionEncodingComboBox("EncodingForC", &m_options.m_pEncodingC, page, this); gbox->addWidget(m_pEncodingCComboBox, line, 1); m_pAutoDetectUnicodeC = new OptionCheckBox(i18n("Auto Detect Unicode"), true, "AutoDetectUnicodeC", &m_options.m_bAutoDetectUnicodeC, page, this); gbox->addWidget(m_pAutoDetectUnicodeC, line, 2); m_pAutoDetectUnicodeC->setToolTip(autoDetectToolTip); ++line; label = new QLabel(i18n("File Encoding for Merge Output and Saving:"), page); gbox->addWidget(label, line, 0); m_pEncodingOutComboBox = new OptionEncodingComboBox("EncodingForOutput", &m_options.m_pEncodingOut, page, this); gbox->addWidget(m_pEncodingOutComboBox, line, 1); m_pAutoSelectOutEncoding = new OptionCheckBox(i18n("Auto Select"), true, "AutoSelectOutEncoding", &m_options.m_bAutoSelectOutEncoding, page, this); gbox->addWidget(m_pAutoSelectOutEncoding, line, 2); m_pAutoSelectOutEncoding->setToolTip(i18n( "If enabled then the encoding from the input files is used.\n" "In ambiguous cases a dialog will ask the user to choose the encoding for saving.")); ++line; label = new QLabel(i18n("File Encoding for Preprocessor Files:"), page); gbox->addWidget(label, line, 0); m_pEncodingPPComboBox = new OptionEncodingComboBox("EncodingForPP", &m_options.m_pEncodingPP, page, this); gbox->addWidget(m_pEncodingPPComboBox, line, 1); ++line; connect(m_pSameEncoding, &OptionCheckBox::toggled, this, &OptionDialog::slotEncodingChanged); connect(m_pEncodingAComboBox, static_cast(&OptionEncodingComboBox::activated), this, &OptionDialog::slotEncodingChanged); connect(m_pAutoDetectUnicodeA, &OptionCheckBox::toggled, this, &OptionDialog::slotEncodingChanged); connect(m_pAutoSelectOutEncoding, &OptionCheckBox::toggled, this, &OptionDialog::slotEncodingChanged); OptionCheckBox* pRightToLeftLanguage = new OptionCheckBox(i18n("Right To Left Language"), false, "RightToLeftLanguage", &m_options.m_bRightToLeftLanguage, page, this); gbox->addWidget(pRightToLeftLanguage, line, 0, 1, 2); pRightToLeftLanguage->setToolTip(i18n( "Some languages are read from right to left.\n" "This setting will change the viewer and editor accordingly.")); ++line; topLayout->addStretch(10); } -// TODO: Integrate this properly in the build system. -// Currently breaks compilation on Windows b/c of: -// src\ccInstHelper.cpp(15): fatal error C1083: Cannot open include file: 'C:/Program Files/NSIS/Contrib/ExDll/exdll.h': No such file or directory -#define ENABLE_CC_INTEGRATION 0 - -#if ENABLE_CC_INTEGRATION -#include "ccInstHelper.cpp" -#endif - void OptionDialog::setupIntegrationPage(void) { QFrame* page = new QFrame(); KPageWidgetItem* pageItem = new KPageWidgetItem(page, i18n("Integration")); pageItem->setHeader(i18n("Integration Settings")); pageItem->setIcon(QIcon::fromTheme(QStringLiteral("utilities-terminal"))); addPage(pageItem); QVBoxLayout* topLayout = new QVBoxLayout(page); topLayout->setMargin(5); QGridLayout* gbox = new QGridLayout(); gbox->setColumnStretch(2, 5); topLayout->addLayout(gbox); int line = 0; QLabel* label; label = new QLabel(i18n("Command line options to ignore:"), page); gbox->addWidget(label, line, 0); OptionLineEdit* pIgnorableCmdLineOptions = new OptionLineEdit("-u;-query;-html;-abort", "IgnorableCmdLineOptions", &m_options.m_ignorableCmdLineOptions, page, this); gbox->addWidget(pIgnorableCmdLineOptions, line, 1, 1, 2); label->setToolTip(i18n( "List of command line options that should be ignored when KDiff3 is used by other tools.\n" "Several values can be specified if separated via ';'\n" "This will suppress the \"Unknown option\" error.")); ++line; OptionCheckBox* pEscapeKeyQuits = new OptionCheckBox(i18n("Quit also via Escape key"), false, "EscapeKeyQuits", &m_options.m_bEscapeKeyQuits, page, this); gbox->addWidget(pEscapeKeyQuits, line, 0, 1, 2); pEscapeKeyQuits->setToolTip(i18n( "Fast method to exit.\n" "For those who are used to using the Escape key.")); ++line; -#if ENABLE_CC_INTEGRATION - QPushButton* pIntegrateWithClearCase = new QPushButton(i18n("Integrate with ClearCase"), page); - gbox->addWidget(pIntegrateWithClearCase, line, 0); - pIntegrateWithClearCase->setToolTip(i18n( - "Integrate with Rational ClearCase from IBM.\n" - "Modifies the \"map\" file in ClearCase subdir \"lib/mgrs\"\n" - "(Only enabled when ClearCase \"bin\" directory is in the path.)")); - connect(pIntegrateWithClearCase, &QPushButton::clicked, this, &OptionDialog::slotIntegrateWithClearCase); - pIntegrateWithClearCase->setEnabled(integrateWithClearCase("existsClearCase", "") != 0); - - QPushButton* pRemoveClearCaseIntegration = new QPushButton(i18n("Remove ClearCase Integration"), page); - gbox->addWidget(pRemoveClearCaseIntegration, line, 1); - pRemoveClearCaseIntegration->setToolTip(i18n( - "Restore the old \"map\" file from before doing the ClearCase integration.")); - connect(pRemoveClearCaseIntegration, &QPushButton::clicked, this, &OptionDialog::slotRemoveClearCaseIntegration); - pRemoveClearCaseIntegration->setEnabled(integrateWithClearCase("existsClearCase", "") != 0); - - ++line; -#endif - topLayout->addStretch(10); } -void OptionDialog::slotIntegrateWithClearCase() -{ -#if ENABLE_CC_INTEGRATION - char kdiff3CommandPath[1000]; - GetModuleFileNameA(0, kdiff3CommandPath, sizeof(kdiff3CommandPath) - 1); - integrateWithClearCase("install", kdiff3CommandPath); -#endif -} - -void OptionDialog::slotRemoveClearCaseIntegration() -{ -#if ENABLE_CC_INTEGRATION - char kdiff3CommandPath[1000]; - GetModuleFileNameA(0, kdiff3CommandPath, sizeof(kdiff3CommandPath) - 1); - integrateWithClearCase("uninstall", kdiff3CommandPath); -#endif -} void OptionDialog::slotEncodingChanged() { if(m_pSameEncoding->isChecked()) { m_pEncodingBComboBox->setEnabled(false); m_pEncodingBComboBox->setCurrentIndex(m_pEncodingAComboBox->currentIndex()); m_pEncodingCComboBox->setEnabled(false); m_pEncodingCComboBox->setCurrentIndex(m_pEncodingAComboBox->currentIndex()); m_pEncodingOutComboBox->setEnabled(false); m_pEncodingOutComboBox->setCurrentIndex(m_pEncodingAComboBox->currentIndex()); m_pEncodingPPComboBox->setEnabled(false); m_pEncodingPPComboBox->setCurrentIndex(m_pEncodingAComboBox->currentIndex()); m_pAutoDetectUnicodeB->setEnabled(false); m_pAutoDetectUnicodeB->setCheckState(m_pAutoDetectUnicodeA->checkState()); m_pAutoDetectUnicodeC->setEnabled(false); m_pAutoDetectUnicodeC->setCheckState(m_pAutoDetectUnicodeA->checkState()); m_pAutoSelectOutEncoding->setEnabled(false); m_pAutoSelectOutEncoding->setCheckState(m_pAutoDetectUnicodeA->checkState()); } else { m_pEncodingBComboBox->setEnabled(true); m_pEncodingCComboBox->setEnabled(true); m_pEncodingOutComboBox->setEnabled(true); m_pEncodingPPComboBox->setEnabled(true); m_pAutoDetectUnicodeB->setEnabled(true); m_pAutoDetectUnicodeC->setEnabled(true); m_pAutoSelectOutEncoding->setEnabled(true); m_pEncodingOutComboBox->setEnabled(m_pAutoSelectOutEncoding->checkState() == Qt::Unchecked); } } void OptionDialog::setupKeysPage(void) { //QVBox *page = addVBoxPage( i18n("Keys"), i18n("KeyDialog" ), // BarIcon("fonts", KIconLoader::SizeMedium ) ); //QVBoxLayout *topLayout = new QVBoxLayout( page, 0, spacingHint() ); // new KFontChooser( page,"font",false/*onlyFixed*/,QStringList(),false,6 ); //m_pKeyDialog=new KKeyDialog( false, 0 ); //topLayout->addWidget( m_pKeyDialog ); } void OptionDialog::slotOk(void) { slotApply(); accept(); } /** Copy the values from the widgets to the public variables.*/ void OptionDialog::slotApply(void) { std::list::iterator i; for(i = m_optionItemList.begin(); i != m_optionItemList.end(); ++i) { (*i)->apply(); } emit applyDone(); #ifdef Q_OS_WIN QString locale = m_options.m_language; if(locale == "Auto" || locale.isEmpty()) locale = QLocale::system().name().left(2); int spacePos = locale.indexOf(' '); if(spacePos > 0) locale = locale.left(spacePos); QSettings settings("HKEY_CURRENT_USER\\Software\\KDiff3\\diff-ext", QSettings::NativeFormat); settings.setValue("Language", locale); #endif } /** Set the default values in the widgets only, while the public variables remain unchanged. */ void OptionDialog::slotDefault() { int result = KMessageBox::warningContinueCancel(this, i18n("This resets all options. Not only those of the current topic.")); if(result == KMessageBox::Cancel) return; else resetToDefaults(); } void OptionDialog::resetToDefaults() { std::list::iterator i; for(i = m_optionItemList.begin(); i != m_optionItemList.end(); ++i) { (*i)->setToDefault(); } slotEncodingChanged(); } /** Initialise the widgets using the values in the public varibles. */ void OptionDialog::setState() { std::list::iterator i; for(i = m_optionItemList.begin(); i != m_optionItemList.end(); ++i) { (*i)->setToCurrent(); } slotEncodingChanged(); } class ConfigValueMap : public ValueMap { private: KConfigGroup m_config; public: explicit ConfigValueMap(const KConfigGroup& config) : m_config(config) {} void writeEntry(const QString& s, const QFont& v) override { m_config.writeEntry(s, v); } void writeEntry(const QString& s, const QColor& v) override { m_config.writeEntry(s, v); } void writeEntry(const QString& s, const QSize& v) override { m_config.writeEntry(s, v); } void writeEntry(const QString& s, const QPoint& v) override { m_config.writeEntry(s, v); } void writeEntry(const QString& s, int v) override { m_config.writeEntry(s, v); } void writeEntry(const QString& s, bool v) override { m_config.writeEntry(s, v); } void writeEntry(const QString& s, const QString& v) override { m_config.writeEntry(s, v); } void writeEntry(const QString& s, const char* v) override { m_config.writeEntry(s, v); } QFont readFontEntry(const QString& s, const QFont* defaultVal) override { return m_config.readEntry(s, *defaultVal); } QColor readColorEntry(const QString& s, const QColor* defaultVal) override { return m_config.readEntry(s, *defaultVal); } QSize readSizeEntry(const QString& s, const QSize* defaultVal) override { return m_config.readEntry(s, *defaultVal); } QPoint readPointEntry(const QString& s, const QPoint* defaultVal) override { return m_config.readEntry(s, *defaultVal); } bool readBoolEntry(const QString& s, bool defaultVal) override { return m_config.readEntry(s, defaultVal); } int readNumEntry(const QString& s, int defaultVal) override { return m_config.readEntry(s, defaultVal); } QString readStringEntry(const QString& s, const QString& defaultVal) override { return m_config.readEntry(s, defaultVal); } void writeEntry(const QString& s, const QStringList& v) override { m_config.writeEntry(s, v); } QStringList readListEntry(const QString& s, const QStringList& def) override { return m_config.readEntry(s, def); } }; void OptionDialog::saveOptions(KSharedConfigPtr config) { // No i18n()-Translations here! ConfigValueMap cvm(config->group(KDIFF3_CONFIG_GROUP)); std::list::iterator i; for(i = m_optionItemList.begin(); i != m_optionItemList.end(); ++i) { (*i)->doUnpreserve(); (*i)->write(&cvm); } } void OptionDialog::readOptions(KSharedConfigPtr config) { // No i18n()-Translations here! ConfigValueMap cvm(config->group(KDIFF3_CONFIG_GROUP)); std::list::iterator i; for(i = m_optionItemList.begin(); i != m_optionItemList.end(); ++i) { (*i)->read(&cvm); } setState(); } QString OptionDialog::parseOptions(const QStringList& optionList) { QString result; QStringList::const_iterator i; for(i = optionList.begin(); i != optionList.end(); ++i) { QString s = *i; int pos = s.indexOf('='); if(pos > 0) // seems not to have a tag { QString key = s.left(pos); QString val = s.mid(pos + 1); std::list::iterator j; bool bFound = false; for(j = m_optionItemList.begin(); j != m_optionItemList.end(); ++j) { if((*j)->getSaveName() == key) { (*j)->doPreserve(); ValueMap config; config.writeEntry(key, val); // Write the value as a string and (*j)->read(&config); // use the internal conversion from string to the needed value. bFound = true; break; } } if(!bFound) { result += "No config item named \"" + key + "\"\n"; } } else { result += "No '=' found in \"" + s + "\"\n"; } } return result; } QString OptionDialog::calcOptionHelp() { ValueMap config; std::list::iterator j; for(j = m_optionItemList.begin(); j != m_optionItemList.end(); ++j) { (*j)->write(&config); } return config.getAsString(); } void OptionDialog::slotHistoryMergeRegExpTester() { RegExpTester dlg(this, s_autoMergeRegExpToolTip, s_historyStartRegExpToolTip, s_historyEntryStartRegExpToolTip, s_historyEntryStartSortKeyOrderToolTip); dlg.init(m_pAutoMergeRegExpLineEdit->currentText(), m_pHistoryStartRegExpLineEdit->currentText(), m_pHistoryEntryStartRegExpLineEdit->currentText(), m_pHistorySortKeyOrderLineEdit->currentText()); if(dlg.exec()) { m_pAutoMergeRegExpLineEdit->setEditText(dlg.autoMergeRegExp()); m_pHistoryStartRegExpLineEdit->setEditText(dlg.historyStartRegExp()); m_pHistoryEntryStartRegExpLineEdit->setEditText(dlg.historyEntryStartRegExp()); m_pHistorySortKeyOrderLineEdit->setEditText(dlg.historySortKeyOrder()); } } #include "optiondialog.moc" diff --git a/src/optiondialog.h b/src/optiondialog.h index e972950..d1c145d 100644 --- a/src/optiondialog.h +++ b/src/optiondialog.h @@ -1,134 +1,132 @@ /* * kdiff3 - Text Diff And Merge Tool * Copyright (C) 2002-2007 Joachim Eibl, joachim.eibl at gmx.de * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Steet, Fifth Floor, Boston, MA 02110-1301, USA. * */ #ifndef OPTION_DIALOG_H #define OPTION_DIALOG_H #include #include #include #include #include "options.h" class QLabel; class QPlainTextEdit; class OptionItem; class OptionCheckBox; class OptionEncodingComboBox; class OptionLineEdit; class KKeyDialog; class OptionDialog : public KPageDialog { Q_OBJECT public: OptionDialog( bool bShowDirMergeSettings, QWidget *parent = nullptr, char *name = nullptr ); ~OptionDialog( void ) override; QString parseOptions( const QStringList& optionList ); QString calcOptionHelp(); Options m_options; void saveOptions(KSharedConfigPtr config); void readOptions(KSharedConfigPtr config); void setState(); // Must be called before calling exec(); void addOptionItem(OptionItem*); KKeyDialog* m_pKeyDialog; protected Q_SLOTS: virtual void slotDefault( void ); virtual void slotOk( void ); virtual void slotApply( void ); //virtual void buttonClicked( QAbstractButton* ); virtual void helpRequested(); void slotEncodingChanged(); void slotHistoryMergeRegExpTester(); - void slotIntegrateWithClearCase(); - void slotRemoveClearCaseIntegration(); Q_SIGNALS: void applyDone(); private: void resetToDefaults(); std::list m_optionItemList; //QDialogButtonBox *mButtonBox; OptionCheckBox* m_pSameEncoding; OptionEncodingComboBox* m_pEncodingAComboBox; OptionCheckBox* m_pAutoDetectUnicodeA; OptionEncodingComboBox* m_pEncodingBComboBox; OptionCheckBox* m_pAutoDetectUnicodeB; OptionEncodingComboBox* m_pEncodingCComboBox; OptionCheckBox* m_pAutoDetectUnicodeC; OptionEncodingComboBox* m_pEncodingOutComboBox; OptionCheckBox* m_pAutoSelectOutEncoding; OptionEncodingComboBox* m_pEncodingPPComboBox; OptionCheckBox* m_pHistoryAutoMerge; OptionLineEdit* m_pAutoMergeRegExpLineEdit; OptionLineEdit* m_pHistoryStartRegExpLineEdit; OptionLineEdit* m_pHistoryEntryStartRegExpLineEdit; OptionCheckBox* m_pHistoryMergeSorting; OptionLineEdit* m_pHistorySortKeyOrderLineEdit; private: void setupFontPage(); void setupColorPage(); void setupEditPage(); void setupDiffPage(); void setupMergePage(); void setupDirectoryMergePage(); void setupKeysPage(); void setupRegionalPage(); void setupIntegrationPage(); void setupOtherOptions(); }; class FontChooser : public QGroupBox { Q_OBJECT QFont m_font; QPushButton* m_pSelectFont; QPlainTextEdit* m_pExampleTextEdit; QLabel* m_pLabel; public: explicit FontChooser( QWidget* pParent ); QFont font(); void setFont( const QFont&, bool ); private slots: void slotSelectFont(); }; #endif diff --git a/windows_installer/README_WIN.txt b/windows_installer/README_WIN.txt index 2e0d5cb..5cdc0ca 100644 --- a/windows_installer/README_WIN.txt +++ b/windows_installer/README_WIN.txt @@ -1,132 +1,123 @@ KDiff3-Readme for Windows ========================= Author: Joachim Eibl (joachim.eibl@gmx.de) Copyright: (C) 2002-2009 by Joachim Eibl KDiff3-Version: 0.9.93 Homepage: http://kdiff3.sourceforge.net KDiff3 is a program that - compares and merges two or three input files or directories, - shows the differences line by line and character by character (!), - provides an automatic merge-facility and - an integrated editor for comfortable solving of merge-conflicts - and has an intuitive graphical user interface. Now KDiff3-strings are translated into some languages by the KDE-I18N-team. (*.qm-files in the KDiff3-directory) See the Changelog.txt for a list of fixed bugs and new features. Windows-specific information for the precompiled KDiff3 version: ================================================================ This executable is provided for the convenience of users who don't have a compiler at hand. You may redistribute it under the terms of the GNU GENERAL PUBLIC LICENCE. Note that there is NO WARRANTY for this program. Installation: - The installer was initially created by Sebastien Fricker (sebastien.fricker@web.de). It is based on the Nullsoft Scriptable Install System (http://nsis.sourceforge.net) - You can place the directory where you want it. But don't separate the file kdiff3.exe from the others, since they are needed for correct execution. (Using kdiff3.exe standalone is possible except for translations and help.) - Integration with WinCVS: When selected the installer sets KDiff3 to be the default diff-tool for WinCVS if available. Registry HKEY_CURRENT_USER\Software\WinCvs\wincvs\CVS settings: "P_Extdiff" and "P_DiffUseExtDiff" - Integration with TortoiseSVN: When selected the installer sets KDiff3 to be the default diff-tool for TortoiseSVN if available. Registry HKEY_CURRENT_USER\Software\TortoiseSVN: "Diff" and "Merge" - Integration with Explorer (1): When selected KDiff3 will be added to the "Send To" menu in the context menu. If you then select two files or two directories and choose "Send To"->"KDiff3" then KDiff3 will start and compare the specified files. - Integration with Explorer (2): When selected Diff-Ext-For-KDiff3 will be installed. This is a Shell-Extension which adds an entry "KDiff3" into the context menu of Windows Explorer. (e.g. when right-clicking a file or directory) With this it is possible to select files and directories sequentially and in separate directories for comparison with KDiff3. This is based on Diff-Ext by Sergey Zorin (http://diff-ext.sourceforge.net) with extensions for KDiff3 by Joachim Eibl. This extension is not under GPL but under a BSD-style licence. (See file DIFF-EXT-LICENSE.txt.) - SVN Merge Tool: Allows to use KDiff3 for explicit graphical merges with Subversion. This installation option copies a file diff3_cmd.bat into your Application Data subdirectory. (C:\Documents and Settings\Username\Application Data\Subversion\diff3_cmd.bat) (Installation is disabled by default) -- Integration with Rational ClearCase from IBM: Allows to use KDiff3 as comparison - and merge tool for text files under Clearcase. KDiff3 tries to locate the "map"-file - (e.g.: C:\Program Files\Rational\Clearcase\lib\mgrs\map) which tells clearcase - which tool to use for which filetype and operation. KDiff3 stores a backup in - map.preKDiff3Install (if is doesn't exist yet) and modifies the map file so that - KDiff3 is used for text files. On KDiff3-uninstallation the entries containing - "KDiff3" are restored. The map-file is normal text, so you can also adjust it - yourself. (Installation for ClearCase is disabled by default) - Since this program was actually developed for GNU/Linux, there might be Windows specific problems I don't know of yet. Please write me about problems you encounter. Known bugs: - Links are not handled correctly. (This is because links in Windows are not the same as under Un*x-filesystems.) Licence: GNU GENERAL PUBLIC LICENSE, Version 2, June 1991 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You 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 For details see file "COPYING". Exception from the GPL: As a special exception, the copyright holder Joachim Eibl gives permission to link this program with the Qt-library (commercial or non-commercial edition) from Trolltech (www.trolltech.com), and he permits to distribute the resulting executable, without including the source code for the Qt-library in the source distribution. Start from commandline: - Comparing 2 files: kdiff3 file1 file2 - Merging 2 files: kdiff3 file1 file2 -o outputfile - Comparing 3 files: kdiff3 file1 file2 file3 - Merging 3 files: kdiff3 file1 file2 file3 -o outputfile Note that file1 will be treated as base of file2 and file3. If all files have the same name but are in different directories, you can reduce typework by specifying the filename only for the first file. E.g.: - Comparing 3 files: kdiff3 dir1/filename dir2 dir3 (This also works in the open-dialog.) - Comparing 2 directories: kdiff3 dir1 dir2 - Merging 2 directories: kdiff3 dir1 dir2-o destinationdir - Comparing 3 directories: kdiff3 dir1 dir2 dir3 - Merging 3 directories: kdiff3 dir1 dir2 dir3 -o destinationdir (Please read the documentation about comparing/merging directories, especially before you start merging.) If you start without arguments, then a dialog will appear where you can select your files and directories via a filebrowser. For more documentation, see the help-menu or the subdirectory doc. Have fun! diff --git a/windows_installer/ccinstallhelper/README.txt b/windows_installer/ccinstallhelper/README.txt deleted file mode 100644 index c2beffe..0000000 --- a/windows_installer/ccinstallhelper/README.txt +++ /dev/null @@ -1,29 +0,0 @@ -Clearcase install helper plugin DLL-Readme -========================================== -Copyright (C) 2007 Joachim Eibl, All rights reserved. -License: GPL Version 2 or any later version. - -This subdirectory contains files to build a NSIS-plugin which is -called during installation and uninstallation. - -The plugin tries to locate clearcase by looking for cleartool.exe in the -PATH environment variable. - -Then it searches for the "map" file relative to the cleartool.exe in -../lib/mgrs/map. - -This file contains a map which tells clearcase which tool to use for a certain -operation. It is readable and modifiable with any text editor. - -During installation this plugin replaces several entries with the -KDiff3-executable. -The original file is renamed into "map.preKDiff3Install". - -During uninstallation the plugin restores the original contents from the -"map.preKDiff3Install" but only for those entries where KDiff3 is used. - -NSIS integrates the plugin as part of the installer executable and in the -"Uninstall.exe" file. - - - diff --git a/windows_installer/ccinstallhelper/ccInstHelper.cpp b/windows_installer/ccinstallhelper/ccInstHelper.cpp deleted file mode 100644 index 12cf1a8..0000000 --- a/windows_installer/ccinstallhelper/ccInstHelper.cpp +++ /dev/null @@ -1,327 +0,0 @@ -// uninstallHelper.cpp : Defines the entry point for the console application. -// -#define _CRT_SECURE_NO_DEPRECATE -#include "stdafx.h" -#include -#include -#include -#include -#include -#include -#include - -//#define __stdcall - -// For compilation download the NSIS source package and modify the following -// line to point to the exdll.h-file -#include "C:/Programme/NSIS/Contrib/ExDll/exdll.h" - -struct ReplacementItem -{ char* fileType; char* operationType; }; - -ReplacementItem g_replacementTable[] = { - "text_file_delta", "xcompare", - "text_file_delta", "xmerge", - "whole_copy", "xcompare", - "whole_copy", "xmerge", - "z_text_file_delta", "xcompare", - "z_text_file_delta", "xmerge", - "z_whole_copy", "xcompare", - "z_whole_copy", "xmerge", - "_xml", "xcompare", - "_xml", "xmerge", - "_xml2", "xcompare", - "_xml2", "xmerge", - "_rftdef", "xcompare", - "_rftmap", "xcompare", - "_rftvp", "xcompare", - "_xtools", "xcompare", - 0,0 -}; - -struct LineItem -{ - std::string fileType; - std::string opType; - std::string command; - std::string fileOpPart; -}; - -// Return true if successful, else false -bool readAndParseMapFile( const std::string& filename, std::list& lineItemList ) -{ - // Read file - FILE* pFile = fopen( filename.c_str(), "r" ); - if (pFile) - { - fseek(pFile,0,SEEK_END); - int size = ftell(pFile); - fseek(pFile,0,SEEK_SET); - std::vector buf( size ); - fread( &buf[0], 1, size, pFile ); - fclose( pFile ); - - // Replace strings - int lineStartPos=0; - int wordInLine = 0; - LineItem lineItem; - for( int i=0; i& lineItemList ) -{ - FILE* pFile = fopen( filename.c_str(), "w" ); - if (pFile) - { - std::list::const_iterator i = lineItemList.begin(); - for( ; i!=lineItemList.end(); ++i ) - { - const LineItem& li = *i; - fprintf( pFile, "%s%s\n", li.fileOpPart.c_str(), li.command.c_str() ); - } - fclose( pFile ); - } - else - { - return false; - } - return true; -} - -std::string toUpper( const std::string& s ) -{ - std::string s2 = s; - - for( unsigned int i=0; i lineItemList; - bool bSuccess = readAndParseMapFile( path, lineItemList ); - if ( !bSuccess ) - { - std::cerr << "Error reading original map file.\n"; - return -1; - } - - // Create backup - if ( access( bakName.c_str(), 0 )!=0 ) // Create backup only if not exists yet - { - if ( rename( path.c_str(), bakName.c_str() ) ) - { - std::cerr << "Error renaming original map file.\n"; - return -1; - } - } - - std::list::iterator i = lineItemList.begin(); - for( ; i!=lineItemList.end(); ++i ) - { - LineItem& li = *i; - for (int j=0;;++j) - { - ReplacementItem& ri = g_replacementTable[j]; - if ( ri.fileType==0 || ri.operationType==0 ) - break; - if ( li.fileType == ri.fileType && li.opType == ri.operationType ) - { - li.command = kdiff3Command.c_str(); - break; - } - } - } - - bSuccess = writeMapFile( path, lineItemList ); - if ( !bSuccess ) - { - if ( rename( bakName.c_str(), path.c_str() ) ) - std::cerr << "Error writing new map file, restoring old file also failed.\n"; - else - std::cerr << "Error writing new map file, old file restored.\n"; - - return -1; - } - } - else if ( installCommand == _T("uninstall") ) - { - std::list lineItemList; - bool bSuccess = readAndParseMapFile( path, lineItemList ); - if ( !bSuccess ) - { - std::cerr << "Error reading original map file\n."; - return -1; - } - - std::list lineItemListBak; - bSuccess = readAndParseMapFile( bakName, lineItemListBak ); - if ( !bSuccess ) - { - std::cerr << "Error reading backup map file.\n"; - return -1; - } - - std::list::iterator i = lineItemList.begin(); - for( ; i!=lineItemList.end(); ++i ) - { - LineItem& li = *i; - if ((int)toUpper(li.command).find("KDIFF3")>=0) - { - std::list::const_iterator j = lineItemListBak.begin(); - for (;j!=lineItemListBak.end();++j) - { - const LineItem& bi = *j; // backup iterator - if ( li.fileType == bi.fileType && li.opType == bi.opType ) - { - li.command = bi.command; - break; - } - } - } - } - - bSuccess = writeMapFile( path, lineItemList ); - if ( !bSuccess ) - { - std::cerr << "Error writing map file."; - - return -1; - } - } - } - return 0; -} - -extern "C" -void __declspec(dllexport) nsisPlugin(HWND hwndParent, int string_size, - char *variables, stack_t **stacktop, - extra_parameters *extra) -{ - //g_hwndParent=hwndParent; - - EXDLL_INIT(); - { - std::string param1( g_stringsize, ' ' ); - int retVal = popstring( ¶m1[0] ); - if ( retVal == 0 ) - { - std::string param2( g_stringsize, ' ' ); - retVal = popstring( ¶m2[0] ); - if ( retVal == 0 ) - install( param1.c_str(), param2.c_str() ); - return; - } - std::cerr << "Not enough parameters." << std::endl; - } -} - -/* -int _tmain(int argc, _TCHAR* argv[]) -{ - if ( argc<3 ) - { - std::cout << "This program is needed to install/uninstall KDiff3 for clearcase.\n" - "It tries to patch the map file (clearcase-subdir\\lib\\mgrs\\map)\n" - "Usage 1: ccInstHelper install pathToKdiff3.exe\n" - "Usage 2: ccInstHelper uninstall pathToKdiff3.exe\n" - "Backups of the original map files are created in the dir of the map file.\n"; - } - else - { - return install( argv[1], argv[2] ); - } - - return 0; -} -*/ diff --git a/windows_installer/ccinstallhelper/ccinstallhelper.def b/windows_installer/ccinstallhelper/ccinstallhelper.def deleted file mode 100644 index 732cd6c..0000000 --- a/windows_installer/ccinstallhelper/ccinstallhelper.def +++ /dev/null @@ -1,4 +0,0 @@ -LIBRARY "ccinstallhelper" -EXPORTS - nsisPlugin - diff --git a/windows_installer/kdiff3.nsi b/windows_installer/kdiff3.nsi index df39907..4e4f95d 100755 --- a/windows_installer/kdiff3.nsi +++ b/windows_installer/kdiff3.nsi @@ -1,530 +1,515 @@ ;KDiff3-NSIS configuration ;Based on Modern User Interface example files ;Apdapted for KDiff3 by Sebastien Fricker and Joachim Eibl ;Requires nsis_v2.19 !define KDIFF3_VERSION "0.9.98" !define DIFF_EXT32_CLSID "{9F8528E4-AB20-456E-84E5-3CE69D8720F3}" !define DIFF_EXT64_CLSID "{34471FFB-4002-438b-8952-E4588D0C0FE9}" !ifdef KDIFF3_64BIT !define BITS 64 !else !define BITS 32 !endif !define SetupFileName "KDiff3-${BITS}bit-Setup_${KDIFF3_VERSION}.exe" ;-------------------------------- ;Include Modern UI !include "MUI.nsh" !include "x64.nsh" ;-------------------------------- ;General ;Name and file Name "KDiff3" OutFile ${SetupFileName} ;Default installation folder !ifdef KDIFF3_64BIT ;SetRegView 64 InstallDir "$PROGRAMFILES64\KDiff3" !else InstallDir "$PROGRAMFILES\KDiff3" !endif ;Get installation folder from registry if available InstallDirRegKey HKCU "Software\KDiff3" "" !addplugindir ".\nsisplugins" ;-------------------------------- ;Variables Var MUI_TEMP Var STARTMENU_FOLDER Var DIFF_EXT_CLSID Var DIFF_EXT_ID Var DIFF_EXT_DLL Var DIFF_EXT_OLD_DLL ;-------------------------------- ;Interface Settings !define MUI_ABORTWARNING !define MUI_HEADERIMAGE !define MUI_HEADERIMAGE_BITMAP "kdiff3.bmp" ; optional ;-------------------------------- ;Language Selection Dialog Settings ;Remember the installer language !define MUI_LANGDLL_REGISTRY_ROOT "HKCU" !define MUI_LANGDLL_REGISTRY_KEY "Software\KDiff3" !define MUI_LANGDLL_REGISTRY_VALUENAME "Installer Language" ;-------------------------------- ;Pages ;!insertmacro MUI_PAGE_WELCOME !insertmacro MUI_PAGE_LICENSE $(MUILicense) !insertmacro MUI_PAGE_COMPONENTS !insertmacro MUI_PAGE_DIRECTORY Page custom CustomPageC ;Start Menu Folder Page Configuration !define MUI_STARTMENUPAGE_REGISTRY_ROOT "HKCU" !define MUI_STARTMENUPAGE_REGISTRY_KEY "Software\KDiff3" !define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "Start Menu Folder" !insertmacro MUI_PAGE_STARTMENU Application $STARTMENU_FOLDER !insertmacro MUI_PAGE_INSTFILES !define MUI_FINISHPAGE_RUN KDiff3.exe !define MUI_FINISHPAGE_RUN_NOTCHECKED !define MUI_FINISHPAGE_SHOWREADME README_WIN.txt !define MUI_FINISHPAGE_SHOWREADME_CHECKED !insertmacro MUI_PAGE_FINISH !insertmacro MUI_UNPAGE_CONFIRM !insertmacro MUI_UNPAGE_INSTFILES ;-------------------------------- ;Languages !insertmacro MUI_LANGUAGE "English" # first language is the default language !insertmacro MUI_LANGUAGE "French" !insertmacro MUI_LANGUAGE "German" !insertmacro MUI_LANGUAGE "Spanish" !insertmacro MUI_LANGUAGE "SimpChinese" !insertmacro MUI_LANGUAGE "TradChinese" !insertmacro MUI_LANGUAGE "Japanese" !insertmacro MUI_LANGUAGE "Korean" !insertmacro MUI_LANGUAGE "Italian" !insertmacro MUI_LANGUAGE "Dutch" !insertmacro MUI_LANGUAGE "Danish" !insertmacro MUI_LANGUAGE "Swedish" !insertmacro MUI_LANGUAGE "Norwegian" !insertmacro MUI_LANGUAGE "Finnish" !insertmacro MUI_LANGUAGE "Greek" !insertmacro MUI_LANGUAGE "Russian" !insertmacro MUI_LANGUAGE "Portuguese" !insertmacro MUI_LANGUAGE "PortugueseBR" !insertmacro MUI_LANGUAGE "Polish" !insertmacro MUI_LANGUAGE "Ukrainian" !insertmacro MUI_LANGUAGE "Czech" !insertmacro MUI_LANGUAGE "Slovak" !insertmacro MUI_LANGUAGE "Croatian" !insertmacro MUI_LANGUAGE "Bulgarian" !insertmacro MUI_LANGUAGE "Hungarian" !insertmacro MUI_LANGUAGE "Thai" !insertmacro MUI_LANGUAGE "Romanian" !insertmacro MUI_LANGUAGE "Latvian" !insertmacro MUI_LANGUAGE "Macedonian" !insertmacro MUI_LANGUAGE "Estonian" !insertmacro MUI_LANGUAGE "Turkish" !insertmacro MUI_LANGUAGE "Lithuanian" !insertmacro MUI_LANGUAGE "Catalan" !insertmacro MUI_LANGUAGE "Slovenian" !insertmacro MUI_LANGUAGE "Serbian" !insertmacro MUI_LANGUAGE "SerbianLatin" !insertmacro MUI_LANGUAGE "Arabic" !insertmacro MUI_LANGUAGE "Farsi" !insertmacro MUI_LANGUAGE "Hebrew" !insertmacro MUI_LANGUAGE "Indonesian" !insertmacro MUI_LANGUAGE "Mongolian" !insertmacro MUI_LANGUAGE "Luxembourgish" !insertmacro MUI_LANGUAGE "Albanian" !insertmacro MUI_LANGUAGE "Breton" !insertmacro MUI_LANGUAGE "Belarusian" !insertmacro MUI_LANGUAGE "Icelandic" !insertmacro MUI_LANGUAGE "Malay" !insertmacro MUI_LANGUAGE "Bosnian" !insertmacro MUI_LANGUAGE "Kurdish" ;-------------------------------- ;License Language String LicenseLangString MUILicense ${LANG_ENGLISH} "COPYING.txt" LicenseLangString MUILicense ${LANG_FRENCH} "COPYING.txt" LicenseLangString MUILicense ${LANG_GERMAN} "COPYING.txt" LicenseLangString MUILicense ${LANG_SPANISH} "COPYING.txt" LicenseLangString MUILicense ${LANG_SIMPCHINESE} "COPYING.txt" LicenseLangString MUILicense ${LANG_TRADCHINESE} "COPYING.txt" LicenseLangString MUILicense ${LANG_JAPANESE} "COPYING.txt" LicenseLangString MUILicense ${LANG_KOREAN} "COPYING.txt" LicenseLangString MUILicense ${LANG_ITALIAN} "COPYING.txt" LicenseLangString MUILicense ${LANG_DUTCH} "COPYING.txt" LicenseLangString MUILicense ${LANG_DANISH} "COPYING.txt" LicenseLangString MUILicense ${LANG_SWEDISH} "COPYING.txt" LicenseLangString MUILicense ${LANG_NORWEGIAN} "COPYING.txt" LicenseLangString MUILicense ${LANG_FINNISH} "COPYING.txt" LicenseLangString MUILicense ${LANG_GREEK} "COPYING.txt" LicenseLangString MUILicense ${LANG_RUSSIAN} "COPYING.txt" LicenseLangString MUILicense ${LANG_PORTUGUESE} "COPYING.txt" LicenseLangString MUILicense ${LANG_PORTUGUESEBR} "COPYING.txt" LicenseLangString MUILicense ${LANG_POLISH} "COPYING.txt" LicenseLangString MUILicense ${LANG_UKRAINIAN} "COPYING.txt" LicenseLangString MUILicense ${LANG_CZECH} "COPYING.txt" LicenseLangString MUILicense ${LANG_SLOVAK} "COPYING.txt" LicenseLangString MUILicense ${LANG_CROATIAN} "COPYING.txt" LicenseLangString MUILicense ${LANG_BULGARIAN} "COPYING.txt" LicenseLangString MUILicense ${LANG_HUNGARIAN} "COPYING.txt" LicenseLangString MUILicense ${LANG_THAI} "COPYING.txt" LicenseLangString MUILicense ${LANG_ROMANIAN} "COPYING.txt" LicenseLangString MUILicense ${LANG_LATVIAN} "COPYING.txt" LicenseLangString MUILicense ${LANG_MACEDONIAN} "COPYING.txt" LicenseLangString MUILicense ${LANG_ESTONIAN} "COPYING.txt" LicenseLangString MUILicense ${LANG_TURKISH} "COPYING.txt" LicenseLangString MUILicense ${LANG_LITHUANIAN} "COPYING.txt" LicenseLangString MUILicense ${LANG_CATALAN} "COPYING.txt" LicenseLangString MUILicense ${LANG_SLOVENIAN} "COPYING.txt" LicenseLangString MUILicense ${LANG_SERBIAN} "COPYING.txt" LicenseLangString MUILicense ${LANG_SERBIANLATIN} "COPYING.txt" LicenseLangString MUILicense ${LANG_ARABIC} "COPYING.txt" LicenseLangString MUILicense ${LANG_FARSI} "COPYING.txt" LicenseLangString MUILicense ${LANG_HEBREW} "COPYING.txt" LicenseLangString MUILicense ${LANG_INDONESIAN} "COPYING.txt" LicenseLangString MUILicense ${LANG_MONGOLIAN} "COPYING.txt" LicenseLangString MUILicense ${LANG_LUXEMBOURGISH} "COPYING.txt" LicenseLangString MUILicense ${LANG_ALBANIAN} "COPYING.txt" LicenseLangString MUILicense ${LANG_BRETON} "COPYING.txt" LicenseLangString MUILicense ${LANG_BELARUSIAN} "COPYING.txt" LicenseLangString MUILicense ${LANG_ICELANDIC} "COPYING.txt" LicenseLangString MUILicense ${LANG_MALAY} "COPYING.txt" LicenseLangString MUILicense ${LANG_BOSNIAN} "COPYING.txt" LicenseLangString MUILicense ${LANG_KURDISH} "COPYING.txt" ;-------------------------------- ;Reserve Files ;These files should be inserted before other files in the data block ;Keep these lines before any File command ;Only for solid compression (by default, solid compression is enabled for BZIP2 and LZMA) !insertmacro MUI_RESERVEFILE_LANGDLL ReserveFile "installForAllUsersPage.ini" !insertmacro MUI_RESERVEFILE_INSTALLOPTIONS ;-------------------------------- ;Variables Var INSTALL_FOR_ALL_USERS ;-------------------------------- ;Installer Sections Section "Software" SecSoftware SectionIn RO ;Read a value from an InstallOptions INI file !insertmacro MUI_INSTALLOPTIONS_READ $INSTALL_FOR_ALL_USERS "installForAllUsersPage.ini" "Field 2" "State" ;Set ShellVarContext: Defines if SHCTX points to HKLM or HKCU StrCmp $INSTALL_FOR_ALL_USERS "0" "" +3 SetShellVarContext current Goto +2 SetShellVarContext all WriteRegStr HKCU "Software\KDiff3" "InstalledForAllUsers" "$INSTALL_FOR_ALL_USERS" ; Make the KDiff3 uninstaller visible via "System Settings: Add or Remove Programs", (Systemsteuerung/Software) WriteRegStr SHCTX "Software\KDiff3" "" "$INSTDIR" WriteRegStr SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\KDiff3" "DisplayName" "KDiff3 (remove only)" WriteRegStr SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\KDiff3" "UninstallString" '"$INSTDIR\Uninstall.exe"' SetOutPath "$INSTDIR" ;ADD YOUR OWN FILES HERE... DetailPrint "Writing files" File "${BITS}bit\kdiff3.exe" File "${BITS}bit\kdiff3.exe.manifest" File "${BITS}bit\qt.conf" File "COPYING.txt" File "Readme_Win.txt" File "ChangeLog.txt" SetOutPath "$INSTDIR\bin" File /r "${BITS}bit\bin\*.*" SetOutPath "$INSTDIR" Delete "$INSTDIR\kdiff3-QT4.exe" ;Store installation folder WriteRegStr HKCU "Software\KDiff3" "" $INSTDIR ;Create uninstaller WriteUninstaller "$INSTDIR\Uninstall.exe" !insertmacro MUI_STARTMENU_WRITE_BEGIN Application ;Create shortcuts CreateDirectory "$SMPROGRAMS\$STARTMENU_FOLDER" CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\KDiff3.lnk" "$INSTDIR\kdiff3.exe" CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\Readme.lnk" "$INSTDIR\Readme_Win.txt" CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\GPL.lnk" "$INSTDIR\Copying.txt" CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\Uninstall.lnk" "$INSTDIR\Uninstall.exe" CreateShortCut "$QUICKLAUNCH\KDiff3.lnk" "$INSTDIR\kdiff3.exe" !insertmacro MUI_STARTMENU_WRITE_END SectionEnd Section "Documentation" SecDocumentation DetailPrint "Writing the documentation" SetOutPath "$INSTDIR" File /r doc CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\Documentation.lnk" "$INSTDIR\doc\index.html" SectionEnd Section "Translations" SecTranslations DetailPrint "Writing the translation messages" SetOutPath "$INSTDIR" File /r translations SectionEnd Section "Utilities" SecUtilities DetailPrint "Writing the command line utilities (GNU sed, diff, diff3, etc.)" SetOutPath "$INSTDIR\bin" File /r "bin\*.*" SectionEnd SubSection "Integration" SecIntegration Section "Explorer" SecIntegrationExplorer DetailPrint "Integration to Explorer" ; WriteRegStr HKCR "Directory\shell\KDiff3" "" '&KDiff3' ; WriteRegStr HKCR "Directory\shell\KDiff3\command" "" '"$INSTDIR\kdiff3.exe" "%1"' CreateShortCut "$SENDTO\KDiff3.lnk" '"$INSTDIR\kdiff3.exe"' SectionEnd Section "Diff-Ext" SecIntegrationDiffExtForKDiff3 DetailPrint "Diff-Ext for KDiff3" SetOutPath "$INSTDIR" ${If} ${RunningX64} StrCpy $DIFF_EXT_CLSID ${DIFF_EXT64_CLSID} StrCpy $DIFF_EXT_DLL "diff_ext_for_kdiff3_64.dll" StrCpy $DIFF_EXT_OLD_DLL "diff_ext_for_kdiff3_64_old.dll" StrCpy $DIFF_EXT_ID "diff-ext-for-kdiff3-64" IfFileExists "$INSTDIR\$DIFF_EXT_OLD_DLL" 0 +2 Delete "$INSTDIR\$DIFF_EXT_OLD_DLL" IfFileExists "$INSTDIR\$DIFF_EXT_DLL" 0 +2 Rename "$INSTDIR\$DIFF_EXT_DLL" "$INSTDIR\$DIFF_EXT_OLD_DLL" File "64bit\diff_ext_for_kdiff3_64.dll" SetRegView 64 WriteRegStr SHCTX "Software\Classes\CLSID\$DIFF_EXT_CLSID" "" "$DIFF_EXT_ID" WriteRegStr SHCTX "Software\Classes\CLSID\$DIFF_EXT_CLSID\InProcServer32" "" "$INSTDIR\$DIFF_EXT_DLL" WriteRegStr SHCTX "Software\Classes\CLSID\$DIFF_EXT_CLSID\InProcServer32" "ThreadingModel" "Apartment" WriteRegStr SHCTX "Software\Classes\*\shellex\ContextMenuHandlers\$DIFF_EXT_ID" "" "$DIFF_EXT_CLSID" WriteRegStr SHCTX "Software\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved" "$DIFF_EXT_CLSID" "$DIFF_EXT_ID" WriteRegStr SHCTX "Software\Classes\Folder\shellex\ContextMenuHandlers\$DIFF_EXT_ID" "" "$DIFF_EXT_CLSID" WriteRegStr SHCTX "Software\Classes\Directory\shellex\ContextMenuHandlers\$DIFF_EXT_ID" "" "$DIFF_EXT_CLSID" SetRegView 32 ${EndIf} StrCpy $DIFF_EXT_CLSID ${DIFF_EXT32_CLSID} StrCpy $DIFF_EXT_DLL "diff_ext_for_kdiff3.dll" StrCpy $DIFF_EXT_OLD_DLL "diff_ext_for_kdiff3_old.dll" StrCpy $DIFF_EXT_ID "diff-ext-for-kdiff3" IfFileExists "$INSTDIR\$DIFF_EXT_OLD_DLL" 0 +2 Delete "$INSTDIR\$DIFF_EXT_OLD_DLL" IfFileExists "$INSTDIR\$DIFF_EXT_DLL" 0 +2 Rename "$INSTDIR\$DIFF_EXT_DLL" "$INSTDIR\$DIFF_EXT_OLD_DLL" File "32bit\diff_ext_for_kdiff3.dll" SetRegView 64 WriteRegStr HKCU "Software\KDiff3\diff-ext" "" "" WriteRegStr SHCTX "Software\KDiff3\diff-ext" "InstallDir" "$INSTDIR" WriteRegStr SHCTX "Software\KDiff3\diff-ext" "diffcommand" "$INSTDIR\kdiff3.exe" SetRegView 32 WriteRegStr SHCTX "Software\Classes\CLSID\$DIFF_EXT_CLSID" "" "$DIFF_EXT_ID" WriteRegStr SHCTX "Software\Classes\CLSID\$DIFF_EXT_CLSID\InProcServer32" "" "$INSTDIR\$DIFF_EXT_DLL" WriteRegStr SHCTX "Software\Classes\CLSID\$DIFF_EXT_CLSID\InProcServer32" "ThreadingModel" "Apartment" WriteRegStr SHCTX "Software\Classes\*\shellex\ContextMenuHandlers\$DIFF_EXT_ID" "" "$DIFF_EXT_CLSID" WriteRegStr SHCTX "Software\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved" "$DIFF_EXT_CLSID" "$DIFF_EXT_ID" WriteRegStr SHCTX "Software\Classes\Folder\shellex\ContextMenuHandlers\$DIFF_EXT_ID" "" "$DIFF_EXT_CLSID" WriteRegStr SHCTX "Software\Classes\Directory\shellex\ContextMenuHandlers\$DIFF_EXT_ID" "" "$DIFF_EXT_CLSID" File "DIFF-EXT-LICENSE.txt" CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\Diff-Ext License.lnk" "$INSTDIR\DIFF-EXT-LICENSE.txt" SectionEnd Section "WinCVS" SecIntegrationWinCVS DetailPrint "Integration to WinCVS" #MessageBox MB_OK "If WinCVS is running, please close it before proceeding." WriteRegStr HKCU "Software\WinCvs\wincvs\CVS settings" "P_Extdiff" '$INSTDIR\kdiff3.exe' WriteRegBin HKCU "Software\WinCvs\wincvs\CVS settings" "P_DiffUseExtDiff" 01 SectionEnd Section "TortoiseSVN" SecIntegrationTortoiseSVN DetailPrint "Integration to TortoiseSVN" WriteRegStr HKCU "Software\TortoiseSVN\" "Diff" '$INSTDIR\kdiff3.exe %base %mine --L1 Base --L2 Mine' WriteRegStr HKCU "Software\TortoiseSVN\" "Merge" '$INSTDIR\kdiff3.exe %base %mine %theirs -o %merged --L1 Base --L2 Mine --L3 Theirs' SectionEnd Section /o "SVN Merge tool" SecIntegrationSubversionDiff3Cmd DetailPrint "Integrate diff3_cmd.bat for Subversion" File "diff3_cmd.bat" CreateDirectory '$APPDATA\Subversion' CopyFiles '$INSTDIR\diff3_cmd.bat' '$APPDATA\Subversion' SectionEnd - -Section /o "ClearCase" SecIntegrationClearCase - DetailPrint "Integrate with Rational ClearCase from IBM" - ccInstallHelper::nsisPlugin "install" "$INSTDIR\kdiff3.exe" - - ;File "ccInstHelper.exe" - ;ExecWait '"$INSTDIR\ccInstHelper.exe" install "$INSTDIR\kdiff3.exe"' -SectionEnd - SubSectionEnd ;-------------------------------- ;Installer Functions Function .onInit !insertmacro MUI_LANGDLL_DISPLAY !insertmacro MUI_INSTALLOPTIONS_EXTRACT "installForAllUsersPage.ini" FunctionEnd Function CustomPageC !insertmacro MUI_HEADER_TEXT "$(TEXT_IO_TITLE)" "$(TEXT_IO_SUBTITLE)" !insertmacro MUI_INSTALLOPTIONS_DISPLAY "installForAllUsersPage.ini" FunctionEnd ;-------------------------------- ;Descriptions ;USE A LANGUAGE STRING IF YOU WANT YOUR DESCRIPTIONS TO BE LANGUAGE SPECIFIC ;Assign descriptions to sections !insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN !insertmacro MUI_DESCRIPTION_TEXT ${SecSoftware} "Main program." !insertmacro MUI_DESCRIPTION_TEXT ${SecDocumentation} "English documentation in HTML-format (Docs for other languages are available on the homepage.)" !insertmacro MUI_DESCRIPTION_TEXT ${SecTranslations} "Translations for visible strings in many languages. Not needed for US-English." !insertmacro MUI_DESCRIPTION_TEXT ${SecUtilities} "Command Line Utilities: GNU sed, diff, diff3, etc. precompiled for Windows" !insertmacro MUI_DESCRIPTION_TEXT ${SecIntegration} "Integrate KDiff3 with certain programs. (See also the Readme for details.)" !insertmacro MUI_DESCRIPTION_TEXT ${SecIntegrationExplorer} "Integrate KDiff3 with Explorer. Adds an entry for KDiff3 in the Send-To context menu." !insertmacro MUI_DESCRIPTION_TEXT ${SecIntegrationDiffExtForKDiff3} "Installs Diff-Ext by Sergey Zorin. Adds entries for KDiff3 in Explorer context menu." !insertmacro MUI_DESCRIPTION_TEXT ${SecIntegrationWinCVS} "Integrate KDiff3 with WinCVS. (Please close WinCVS before proceeding.)" !insertmacro MUI_DESCRIPTION_TEXT ${SecIntegrationTortoiseSVN} "Integrate KDiff3 with TortoiseSVN." !insertmacro MUI_DESCRIPTION_TEXT ${SecIntegrationSubversionDiff3Cmd} "Install diff3_cmd.bat for Subversion merge" - !insertmacro MUI_DESCRIPTION_TEXT ${SecIntegrationClearCase} "Integrate KDiff3 with Rational Clearcase from IBM" !insertmacro MUI_FUNCTION_DESCRIPTION_END ;-------------------------------- ;Uninstaller Section Section "Uninstall" ReadRegStr $INSTALL_FOR_ALL_USERS HKCU "Software\KDiff3" "InstalledForAllUsers" ;Set ShellVarContext: Defines if SHCTX points to HKLM or HKCU StrCmp $INSTALL_FOR_ALL_USERS "0" "" +3 SetShellVarContext current Goto +2 SetShellVarContext all Delete "$INSTDIR\Uninstall.exe" Delete "$INSTDIR\kdiff3.exe" Delete "$INSTDIR\kdiff3.exe.manifest" Delete "$INSTDIR\qt.conf" Delete "$INSTDIR\COPYING.txt" Delete "$INSTDIR\Readme_Win.txt" Delete "$INSTDIR\ChangeLog.txt" Delete "$INSTDIR\diff_ext_for_kdiff3.dll" Delete "$INSTDIR\diff_ext_for_kdiff3_old.dll" Delete "$INSTDIR\diff_ext_for_kdiff3_64.dll" Delete "$INSTDIR\diff_ext_for_kdiff3_64_old.dll" Delete "$INSTDIR\DIFF-EXT-LICENSE.txt" RMDir /r "$INSTDIR\doc" RMDir /r "$INSTDIR\translations" RMDir /r "$INSTDIR\bin" RMDir "$INSTDIR" # without /r the dir is only removed if completely empty !insertmacro MUI_STARTMENU_GETFOLDER Application $MUI_TEMP Delete "$SMPROGRAMS\$MUI_TEMP\Uninstall.lnk" Delete "$SMPROGRAMS\$MUI_TEMP\KDiff3.lnk" Delete "$SMPROGRAMS\$MUI_TEMP\KDiff3-Qt4.lnk" Delete "$SMPROGRAMS\$MUI_TEMP\Readme.lnk" Delete "$SMPROGRAMS\$MUI_TEMP\GPL.lnk" Delete "$SMPROGRAMS\$MUI_TEMP\Diff-Ext License.lnk" Delete "$SMPROGRAMS\$MUI_TEMP\Documentation.lnk" Delete "$QUICKLAUNCH\KDiff3.lnk" Delete "$SENDTO\KDiff3.lnk" ;Delete empty start menu parent diretories StrCpy $MUI_TEMP "$SMPROGRAMS\$MUI_TEMP" startMenuDeleteLoop: ClearErrors RMDir $MUI_TEMP GetFullPathName $MUI_TEMP "$MUI_TEMP\.." IfErrors startMenuDeleteLoopDone StrCmp $MUI_TEMP $SMPROGRAMS startMenuDeleteLoopDone startMenuDeleteLoop startMenuDeleteLoopDone: DeleteRegKey HKCU "Software\KDiff3" DeleteRegKey SHCTX "Software\KDiff3" DeleteRegKey SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\KDiff3" ; diff_ext_for_kdiff3 ${If} ${RunningX64} StrCpy $DIFF_EXT_CLSID ${DIFF_EXT64_CLSID} StrCpy $DIFF_EXT_ID "diff-ext-for-kdiff3-64" SetRegView 64 DeleteRegKey SHCTX "Software\Classes\CLSID\$DIFF_EXT_CLSID" DeleteRegKey SHCTX "Software\Classes\*\shellex\ContextMenuHandlers\$DIFF_EXT_ID" DeleteRegKey SHCTX "Software\Classes\Folder\shellex\ContextMenuHandlers\$DIFF_EXT_ID" DeleteRegKey SHCTX "Software\Classes\Directory\shellex\ContextMenuHandlers\$DIFF_EXT_ID" DeleteRegValue SHCTX "Software\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved" "$DIFF_EXT_CLSID" SetRegView 32 ${EndIf} StrCpy $DIFF_EXT_CLSID ${DIFF_EXT32_CLSID} StrCpy $DIFF_EXT_ID "diff-ext-for-kdiff3" DeleteRegKey SHCTX "Software\Classes\CLSID\$DIFF_EXT_CLSID" DeleteRegKey SHCTX "Software\Classes\*\shellex\ContextMenuHandlers\$DIFF_EXT_ID" DeleteRegKey SHCTX "Software\Classes\Folder\shellex\ContextMenuHandlers\$DIFF_EXT_ID" DeleteRegKey SHCTX "Software\Classes\Directory\shellex\ContextMenuHandlers\$DIFF_EXT_ID" DeleteRegValue SHCTX "Software\Microsoft\Windows\CurrentVersion\Shell Extensions\Approved" "$DIFF_EXT_CLSID" - ; clearcase - ccInstallHelper::nsisPlugin "uninstall" "$INSTDIR\kdiff3.exe" - ;ExecWait '"$INSTDIR\ccInstHelper.exe" uninstall "$INSTDIR\kdiff3.exe"' - ;Delete "$INSTDIR\ccInstHelper.exe" - SectionEnd ;-------------------------------- ;Uninstaller Functions Function un.onInit !insertmacro MUI_UNGETLANGUAGE FunctionEnd