├── .gitignore ├── CHANGES.txt ├── DoubleFileScanner.aps ├── DoubleFileScanner.qrc ├── DoubleFileScanner.rcx ├── DoubleFileScanner.sln ├── DoubleFileScanner.vcxproj ├── DoubleFileScanner.vcxproj.filters ├── LICENSE.html ├── README.txt ├── etc ├── Manifest │ └── Compatibility.manifest └── inifile.exe ├── gui ├── Window_Directories.ui └── Window_Main.ui ├── res ├── Button_About.png ├── Button_Accept.png ├── Button_AutoClean.png ├── Button_Cancel.png ├── Button_Clear.png ├── Button_CopyToClipboard.png ├── Button_Exit.png ├── Button_Export.png ├── Button_FileDelete.png ├── Button_FileGoto.png ├── Button_FileOpen.png ├── Button_FileRename.png ├── Button_FolderAdd.png ├── Button_FolderRemove.png ├── Button_Start.png ├── Button_WebLink.png ├── DoubleFileScanner.ico ├── DoubleFileScanner.png ├── Icon_Bullet.png ├── Icon_Duplicate.png ├── Icon_Folder.png ├── Logo.old ├── Logo.png ├── Sign_Accept.png ├── Sign_Cancel.png ├── Sign_Clocks.png ├── Spinner.GIF └── Spinner.new ├── src ├── Config.cpp ├── Config.h ├── Main.cpp ├── Model_Duplicates.cpp ├── Model_Duplicates.h ├── Resource.h ├── System.cpp ├── System.h ├── Taskbar.cpp ├── Taskbar.h ├── Thread_DirectoryScanner.cpp ├── Thread_DirectoryScanner.h ├── Thread_FileComparator.cpp ├── Thread_FileComparator.h ├── Utilities.cpp ├── Utilities.h ├── Version.h ├── Window_Directories.cpp ├── Window_Directories.h ├── Window_Main.cpp ├── Window_Main.h └── strnatcmp │ ├── README.htm │ ├── strnatcmp.cpp │ └── strnatcmp.h ├── z_build.bat ├── z_drMemory.bat └── z_drMemory.spr /.gitignore: -------------------------------------------------------------------------------- 1 | *.opensdf 2 | *.sdf 3 | *.suo 4 | *.user 5 | *.local.* 6 | *.VC.db 7 | /bin 8 | /ipch 9 | /doc 10 | /tmp 11 | /out -------------------------------------------------------------------------------- /CHANGES.txt: -------------------------------------------------------------------------------- 1 | Double File Scanner 2 | Copyright (C) 2014-2017 LoRd_MuldeR . Some rights reserved. 3 | 4 | ------------------------------------------------------------------------------ 5 | Version History 6 | ------------------------------------------------------------------------------ 7 | 8 | Version 2.04 [2017-04-22] 9 | - Make it possible to suspend/resume the scanning process 10 | - Upgraded Qt Framework libraries to v4.8.7 11 | - Upgraded build environment to Visual Studio 2015.3 12 | 13 | Version 2.03 [2014-06-22] 14 | - Performance optimizations 15 | - Display file name and path in separate columns 16 | - Further improved sorting of the results 17 | - Various minor fixes and improvements 18 | 19 | Version 2.02 [2014-06-20] 20 | - Added automatic clean-up wizard 21 | - Display the number of duplicates per group 22 | - Display the size of each file 23 | - Display tooltips when hovering tree view items 24 | - Various minor fixes and improvements 25 | 26 | Version 2.01 [2014-06-16] 27 | - Fixed Windows XP compatibility 28 | - Added commands to delete or rename a duplicate file 29 | - Improved sorting of the results 30 | - Various minor fixes and improvements 31 | 32 | Version 2.00 [2014-06-14] 33 | - First public release of the v2.xx branch 34 | -------------------------------------------------------------------------------- /DoubleFileScanner.aps: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lordmulder/DoubleFilerScanner/58c47313ac65bbcd6d99422c20922cd847cad688/DoubleFileScanner.aps -------------------------------------------------------------------------------- /DoubleFileScanner.qrc: -------------------------------------------------------------------------------- 1 | 22 | 23 | 24 | 25 | 26 | res/Button_About.png 27 | res/Button_Accept.png 28 | res/Button_AutoClean.png 29 | res/Button_Cancel.png 30 | res/Button_Clear.png 31 | res/Button_CopyToClipboard.png 32 | res/Button_Exit.png 33 | res/Button_Export.png 34 | res/Button_FileDelete.png 35 | res/Button_FileGoto.png 36 | res/Button_FileOpen.png 37 | res/Button_FileRename.png 38 | res/Button_FolderAdd.png 39 | res/Button_FolderRemove.png 40 | res/Button_Start.png 41 | res/Button_WebLink.png 42 | res/DoubleFileScanner.ico 43 | res/DoubleFileScanner.png 44 | res/Icon_Bullet.png 45 | res/Icon_Duplicate.png 46 | res/Icon_Folder.png 47 | res/Logo.png 48 | res/Sign_Accept.png 49 | res/Sign_Cancel.png 50 | res/Sign_Clocks.png 51 | res/Spinner.gif 52 | 53 | 54 | -------------------------------------------------------------------------------- /DoubleFileScanner.rcx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lordmulder/DoubleFilerScanner/58c47313ac65bbcd6d99422c20922cd847cad688/DoubleFileScanner.rcx -------------------------------------------------------------------------------- /DoubleFileScanner.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DoubleFileScanner", "DoubleFileScanner.vcxproj", "{1F2F5657-68CC-4A78-A900-1929DDE3713F}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Win32 = Debug|Win32 11 | Release_Static|Win32 = Release_Static|Win32 12 | Release|Win32 = Release|Win32 13 | EndGlobalSection 14 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 15 | {1F2F5657-68CC-4A78-A900-1929DDE3713F}.Debug|Win32.ActiveCfg = Debug|Win32 16 | {1F2F5657-68CC-4A78-A900-1929DDE3713F}.Debug|Win32.Build.0 = Debug|Win32 17 | {1F2F5657-68CC-4A78-A900-1929DDE3713F}.Release_Static|Win32.ActiveCfg = Release_Static|Win32 18 | {1F2F5657-68CC-4A78-A900-1929DDE3713F}.Release_Static|Win32.Build.0 = Release_Static|Win32 19 | {1F2F5657-68CC-4A78-A900-1929DDE3713F}.Release|Win32.ActiveCfg = Release|Win32 20 | {1F2F5657-68CC-4A78-A900-1929DDE3713F}.Release|Win32.Build.0 = Release|Win32 21 | EndGlobalSection 22 | GlobalSection(SolutionProperties) = preSolution 23 | HideSolutionNode = FALSE 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /DoubleFileScanner.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release_Static 10 | Win32 11 | 12 | 13 | Release 14 | Win32 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | "$(QTDIR)\bin\moc.exe" -o "$(SolutionDir)\tmp\Common\moc\MOC_%(Filename).cpp" "%(FullPath)" 41 | "$(QTDIR)\bin\moc.exe" -o "$(SolutionDir)\tmp\Common\moc\MOC_%(Filename).cpp" "%(FullPath)" 42 | MOC "$(SolutionDir)\tmp\Common\moc\MOC_%(Filename).cpp" 43 | MOC "$(SolutionDir)\tmp\Common\moc\MOC_%(Filename).cpp" 44 | $(SolutionDir)\tmp\Common\moc\MOC_%(Filename).cpp;%(Outputs) 45 | $(SolutionDir)\tmp\Common\moc\MOC_%(Filename).cpp;%(Outputs) 46 | "$(QTDIR)\bin\moc.exe" -o "$(SolutionDir)\tmp\Common\moc\MOC_%(Filename).cpp" "%(FullPath)" 47 | MOC "$(SolutionDir)\tmp\Common\moc\MOC_%(Filename).cpp" 48 | $(SolutionDir)\tmp\Common\moc\MOC_%(Filename).cpp;%(Outputs) 49 | 50 | 51 | 52 | 53 | "$(QTDIR)\bin\moc.exe" -o "$(SolutionDir)\tmp\Common\moc\MOC_%(Filename).cpp" "%(FullPath)" 54 | "$(QTDIR)\bin\moc.exe" -o "$(SolutionDir)\tmp\Common\moc\MOC_%(Filename).cpp" "%(FullPath)" 55 | "$(QTDIR)\bin\moc.exe" -o "$(SolutionDir)\tmp\Common\moc\MOC_%(Filename).cpp" "%(FullPath)" 56 | MOC "$(SolutionDir)\tmp\Common\moc\MOC_%(Filename).cpp" 57 | MOC "$(SolutionDir)\tmp\Common\moc\MOC_%(Filename).cpp" 58 | MOC "$(SolutionDir)\tmp\Common\moc\MOC_%(Filename).cpp" 59 | $(SolutionDir)\tmp\Common\moc\MOC_%(Filename).cpp;%(Outputs) 60 | $(SolutionDir)\tmp\Common\moc\MOC_%(Filename).cpp;%(Outputs) 61 | $(SolutionDir)\tmp\Common\moc\MOC_%(Filename).cpp;%(Outputs) 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | "$(QTDIR)\bin\moc.exe" -o "$(SolutionDir)\tmp\Common\moc\MOC_%(Filename).cpp" "%(FullPath)" 70 | "$(QTDIR)\bin\moc.exe" -o "$(SolutionDir)\tmp\Common\moc\MOC_%(Filename).cpp" "%(FullPath)" 71 | "$(QTDIR)\bin\moc.exe" -o "$(SolutionDir)\tmp\Common\moc\MOC_%(Filename).cpp" "%(FullPath)" 72 | MOC "$(SolutionDir)\tmp\Common\moc\MOC_%(Filename).cpp" 73 | MOC "$(SolutionDir)\tmp\Common\moc\MOC_%(Filename).cpp" 74 | MOC "$(SolutionDir)\tmp\Common\moc\MOC_%(Filename).cpp" 75 | $(SolutionDir)\tmp\Common\moc\MOC_%(Filename).cpp;%(Outputs) 76 | $(SolutionDir)\tmp\Common\moc\MOC_%(Filename).cpp;%(Outputs) 77 | $(SolutionDir)\tmp\Common\moc\MOC_%(Filename).cpp;%(Outputs) 78 | 79 | 80 | "$(QTDIR)\bin\moc.exe" -o "$(SolutionDir)\tmp\Common\moc\MOC_%(Filename).cpp" "%(FullPath)" 81 | "$(QTDIR)\bin\moc.exe" -o "$(SolutionDir)\tmp\Common\moc\MOC_%(Filename).cpp" "%(FullPath)" 82 | "$(QTDIR)\bin\moc.exe" -o "$(SolutionDir)\tmp\Common\moc\MOC_%(Filename).cpp" "%(FullPath)" 83 | MOC "$(SolutionDir)\tmp\Common\moc\MOC_%(Filename).cpp" 84 | MOC "$(SolutionDir)\tmp\Common\moc\MOC_%(Filename).cpp" 85 | MOC "$(SolutionDir)\tmp\Common\moc\MOC_%(Filename).cpp" 86 | $(SolutionDir)\tmp\Common\moc\MOC_%(Filename).cpp;%(Outputs) 87 | $(SolutionDir)\tmp\Common\moc\MOC_%(Filename).cpp;%(Outputs) 88 | $(SolutionDir)\tmp\Common\moc\MOC_%(Filename).cpp;%(Outputs) 89 | 90 | 91 | "$(QTDIR)\bin\moc.exe" -o "$(SolutionDir)\tmp\Common\moc\MOC_%(Filename).cpp" "%(FullPath)" 92 | "$(QTDIR)\bin\moc.exe" -o "$(SolutionDir)\tmp\Common\moc\MOC_%(Filename).cpp" "%(FullPath)" 93 | "$(QTDIR)\bin\moc.exe" -o "$(SolutionDir)\tmp\Common\moc\MOC_%(Filename).cpp" "%(FullPath)" 94 | MOC "$(SolutionDir)\tmp\Common\moc\MOC_%(Filename).cpp" 95 | MOC "$(SolutionDir)\tmp\Common\moc\MOC_%(Filename).cpp" 96 | MOC "$(SolutionDir)\tmp\Common\moc\MOC_%(Filename).cpp" 97 | $(SolutionDir)\tmp\Common\moc\MOC_%(Filename).cpp;%(Outputs) 98 | $(SolutionDir)\tmp\Common\moc\MOC_%(Filename).cpp;%(Outputs) 99 | $(SolutionDir)\tmp\Common\moc\MOC_%(Filename).cpp;%(Outputs) 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | Document 110 | "$(QTDIR)\bin\rcc.exe" -o "$(SolutionDir)\tmp\Common\rcc\RCC_%(Filename).cpp" -name "%(Filename)" "%(FullPath)" 111 | "$(QTDIR)\bin\rcc.exe" -o "$(SolutionDir)\tmp\Common\rcc\RCC_%(Filename).cpp" -name "%(Filename)" "%(FullPath)" 112 | RCC "$(SolutionDir)\tmp\Common\rcc\RCC_%(Filename).cpp" 113 | RCC "$(SolutionDir)\tmp\Common\rcc\RCC_%(Filename).cpp" 114 | $(SolutionDir)\tmp\Common\rcc\RCC_%(Filename).cpp;%(Outputs) 115 | $(SolutionDir)\tmp\Common\rcc\RCC_%(Filename).cpp;%(Outputs) 116 | "$(QTDIR)\bin\rcc.exe" -o "$(SolutionDir)\tmp\Common\rcc\RCC_%(Filename).cpp" -name "%(Filename)" "%(FullPath)" 117 | RCC "$(SolutionDir)\tmp\Common\rcc\RCC_%(Filename).cpp" 118 | $(SolutionDir)\tmp\Common\rcc\RCC_%(Filename).cpp;%(Outputs) 119 | 120 | 121 | 122 | 123 | Document 124 | "$(QTDIR)\bin\uic.exe" -o "$(SolutionDir)\tmp\Common\uic\UIC_%(Filename).h" "%(FullPath)" 125 | "$(QTDIR)\bin\uic.exe" -o "$(SolutionDir)\tmp\Common\uic\UIC_%(Filename).h" "%(FullPath)" 126 | "$(QTDIR)\bin\uic.exe" -o "$(SolutionDir)\tmp\Common\uic\UIC_%(Filename).h" "%(FullPath)" 127 | UIC "$(SolutionDir)\tmp\Common\uic\UIC_%(Filename).h" 128 | UIC "$(SolutionDir)\tmp\Common\uic\UIC_%(Filename).h" 129 | UIC "$(SolutionDir)\tmp\Common\uic\UIC_%(Filename).h" 130 | $(SolutionDir)\tmp\Common\uic\UIC_%(Filename).h;%(Outputs) 131 | $(SolutionDir)\tmp\Common\uic\UIC_%(Filename).h;%(Outputs) 132 | $(SolutionDir)\tmp\Common\uic\UIC_%(Filename).h;%(Outputs) 133 | 134 | 135 | 136 | 137 | Document 138 | "$(QTDIR)\bin\uic.exe" -o "$(SolutionDir)\tmp\Common\uic\UIC_%(Filename).h" "%(FullPath)" 139 | "$(QTDIR)\bin\uic.exe" -o "$(SolutionDir)\tmp\Common\uic\UIC_%(Filename).h" "%(FullPath)" 140 | "$(QTDIR)\bin\uic.exe" -o "$(SolutionDir)\tmp\Common\uic\UIC_%(Filename).h" "%(FullPath)" 141 | UIC "$(SolutionDir)\tmp\Common\uic\UIC_%(Filename).h" 142 | UIC "$(SolutionDir)\tmp\Common\uic\UIC_%(Filename).h" 143 | UIC "$(SolutionDir)\tmp\Common\uic\UIC_%(Filename).h" 144 | $(SolutionDir)\tmp\Common\uic\UIC_%(Filename).h;%(Outputs) 145 | $(SolutionDir)\tmp\Common\uic\UIC_%(Filename).h;%(Outputs) 146 | $(SolutionDir)\tmp\Common\uic\UIC_%(Filename).h;%(Outputs) 147 | 148 | 149 | 150 | 151 | 152 | 153 | {1F2F5657-68CC-4A78-A900-1929DDE3713F} 154 | Win32Proj 155 | DoubleFileScanner 156 | 157 | 158 | 159 | Application 160 | true 161 | v140_xp 162 | Unicode 163 | 164 | 165 | Application 166 | false 167 | v140_xp 168 | true 169 | Unicode 170 | 171 | 172 | Application 173 | false 174 | v140_xp 175 | true 176 | Unicode 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | true 193 | $(SolutionDir)bin\$(Platform)\$(Configuration)\ 194 | $(SolutionDir)tmp\$(Platform)\$(Configuration)\ 195 | 196 | 197 | false 198 | $(SolutionDir)bin\$(Platform)\$(Configuration)\ 199 | $(SolutionDir)tmp\$(Platform)\$(Configuration)\ 200 | 201 | 202 | false 203 | $(SolutionDir)bin\$(Platform)\$(Configuration)\ 204 | $(SolutionDir)tmp\$(Platform)\$(Configuration)\ 205 | 206 | 207 | 208 | 209 | 210 | Level3 211 | Disabled 212 | WIN32;_DEBUG;_CONSOLE;_LIB;QT_DEBUG;%(PreprocessorDefinitions) 213 | $(QTDIR)\include;$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(SolutionDir)\tmp\Common\uic;$(SolutionDir)\..\Prerequisites\VisualLeakDetector\include;%(AdditionalIncludeDirectories) 214 | NoExtensions 215 | 4127 216 | true 217 | Default 218 | Disabled 219 | 220 | 221 | Windows 222 | true 223 | $(SolutionDir)\..\Prerequisites\Qt4\$(PlatformToolset)\Debug\lib;$(SolutionDir)\..\Prerequisites\VisualLeakDetector\lib\Win32 224 | QtCored4.lib;QtGuid4.lib;%(AdditionalDependencies) 225 | win32EntryPoint 226 | 227 | 228 | del $(IntermediateOutputPath)\Config.obj 229 | 230 | 231 | 232 | 233 | Level3 234 | 235 | 236 | Full 237 | true 238 | true 239 | WIN32;NDEBUG;_CONSOLE;_LIB;QT_NO_DEBUG;%(PreprocessorDefinitions) 240 | $(QTDIR)\include;$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(SolutionDir)\tmp\Common\uic;$(SolutionDir)\..\Prerequisites\VisualLeakDetector\include;%(AdditionalIncludeDirectories) 241 | NoExtensions 242 | true 243 | 4127 244 | OnlyExplicitInline 245 | Speed 246 | true 247 | Fast 248 | false 249 | 250 | 251 | Windows 252 | false 253 | true 254 | true 255 | $(SolutionDir)\..\Prerequisites\Qt4\$(PlatformToolset)\Shared\lib 256 | QtCore4.lib;QtGui4.lib;%(AdditionalDependencies) 257 | win32EntryPoint 258 | 259 | 260 | del $(IntermediateOutputPath)\Config.obj 261 | 262 | 263 | 264 | 265 | Level3 266 | 267 | 268 | Full 269 | true 270 | true 271 | WIN32;NDEBUG;_CONSOLE;_LIB;QT_NO_DEBUG;QT_NODLL;%(PreprocessorDefinitions) 272 | $(QTDIR)\include;$(QTDIR)\include\QtCore;$(QTDIR)\include\QtGui;$(SolutionDir)\tmp\Common\uic;$(SolutionDir)\..\Prerequisites\VisualLeakDetector\include;%(AdditionalIncludeDirectories) 273 | NoExtensions 274 | MultiThreaded 275 | true 276 | 4127 277 | OnlyExplicitInline 278 | Speed 279 | true 280 | Fast 281 | false 282 | 283 | 284 | Windows 285 | false 286 | true 287 | true 288 | $(SolutionDir)\..\Prerequisites\EncodePointer\lib;$(SolutionDir)\..\Prerequisites\Qt4\$(PlatformToolset)\Static\lib;$(SolutionDir)\..\Prerequisites\Qt4\$(PlatformToolset)\Static\plugins\imageformats 289 | QtCore.lib;QtGui.lib;QtSvg.lib;qtmain.lib;qsvg.lib;qico.lib;qtga.lib;Winmm.lib;imm32.lib;ws2_32.lib;Shlwapi.lib;EncodePointer.lib;%(AdditionalDependencies) 290 | "/MANIFESTDEPENDENCY:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' publicKeyToken='6595b64144ccf1df' language='*' processorArchitecture='*'" %(AdditionalOptions) 291 | false 292 | win32EntryPoint 293 | 294 | 295 | del $(IntermediateOutputPath)\Config.obj 296 | 297 | 298 | 299 | 300 | 301 | -------------------------------------------------------------------------------- /DoubleFileScanner.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;hm;inl;inc;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | {c742c16f-33e0-4762-8ef4-59c3b45e1bb8} 18 | 19 | 20 | {37daafb0-55f1-4bb0-9a08-6f81e6d3ed76} 21 | 22 | 23 | {bcb9ef9d-13cb-4d08-9d79-5b8bb08c4e48} 24 | 25 | 26 | {a77e9f3e-2ca7-4bc8-ae33-f264316ee29f} 27 | 28 | 29 | 30 | 31 | Source Files 32 | 33 | 34 | Source Files 35 | 36 | 37 | Source Files\Generated\RCC 38 | 39 | 40 | Source Files 41 | 42 | 43 | Source Files 44 | 45 | 46 | Source Files 47 | 48 | 49 | Source Files 50 | 51 | 52 | Source Files\Generated\MOC 53 | 54 | 55 | Source Files\Generated\MOC 56 | 57 | 58 | Source Files\Generated\MOC 59 | 60 | 61 | Source Files 62 | 63 | 64 | Source Files\Generated\MOC 65 | 66 | 67 | Source Files\Generated\MOC 68 | 69 | 70 | Source Files 71 | 72 | 73 | Source Files 74 | 75 | 76 | Source Files 77 | 78 | 79 | Source Files 80 | 81 | 82 | 83 | 84 | Header Files 85 | 86 | 87 | Header Files 88 | 89 | 90 | Header Files 91 | 92 | 93 | Header Files 94 | 95 | 96 | Header Files 97 | 98 | 99 | Header Files 100 | 101 | 102 | Header Files 103 | 104 | 105 | 106 | 107 | Resource Files 108 | 109 | 110 | Header Files 111 | 112 | 113 | Header Files 114 | 115 | 116 | Dialogs 117 | 118 | 119 | Header Files 120 | 121 | 122 | Header Files 123 | 124 | 125 | Header Files 126 | 127 | 128 | Dialogs 129 | 130 | 131 | 132 | 133 | Resource Files 134 | 135 | 136 | 137 | 138 | Resource Files 139 | 140 | 141 | -------------------------------------------------------------------------------- /LICENSE.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | GNU General Public License v2.0 - GNU Project - Free Software Foundation (FSF) 5 | 6 | 7 | 8 |

GNU GENERAL PUBLIC LICENSE

9 |

10 | Version 2, June 1991 11 |

12 | 13 |
Copyright (C) 1989, 1991 Free Software Foundation, Inc.  
 14 | 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
 15 | 
 16 | Everyone is permitted to copy and distribute verbatim copies
 17 | of this license document, but changing it is not allowed.
 18 | 
19 | 20 |

Preamble

21 | 22 |

23 | The licenses for most software are designed to take away your 24 | freedom to share and change it. By contrast, the GNU General Public 25 | License is intended to guarantee your freedom to share and change free 26 | software--to make sure the software is free for all its users. This 27 | General Public License applies to most of the Free Software 28 | Foundation's software and to any other program whose authors commit to 29 | using it. (Some other Free Software Foundation software is covered by 30 | the GNU Lesser General Public License instead.) You can apply it to 31 | your programs, too. 32 |

33 | 34 |

35 | When we speak of free software, we are referring to freedom, not 36 | price. Our General Public Licenses are designed to make sure that you 37 | have the freedom to distribute copies of free software (and charge for 38 | this service if you wish), that you receive source code or can get it 39 | if you want it, that you can change the software or use pieces of it 40 | in new free programs; and that you know you can do these things. 41 |

42 | 43 |

44 | To protect your rights, we need to make restrictions that forbid 45 | anyone to deny you these rights or to ask you to surrender the rights. 46 | These restrictions translate to certain responsibilities for you if you 47 | distribute copies of the software, or if you modify it. 48 |

49 | 50 |

51 | For example, if you distribute copies of such a program, whether 52 | gratis or for a fee, you must give the recipients all the rights that 53 | you have. You must make sure that they, too, receive or can get the 54 | source code. And you must show them these terms so they know their 55 | rights. 56 |

57 | 58 |

59 | We protect your rights with two steps: (1) copyright the software, and 60 | (2) offer you this license which gives you legal permission to copy, 61 | distribute and/or modify the software. 62 |

63 | 64 |

65 | Also, for each author's protection and ours, we want to make certain 66 | that everyone understands that there is no warranty for this free 67 | software. If the software is modified by someone else and passed on, we 68 | want its recipients to know that what they have is not the original, so 69 | that any problems introduced by others will not reflect on the original 70 | authors' reputations. 71 |

72 | 73 |

74 | Finally, any free program is threatened constantly by software 75 | patents. We wish to avoid the danger that redistributors of a free 76 | program will individually obtain patent licenses, in effect making the 77 | program proprietary. To prevent this, we have made it clear that any 78 | patent must be licensed for everyone's free use or not licensed at all. 79 |

80 | 81 |

82 | The precise terms and conditions for copying, distribution and 83 | modification follow. 84 |

85 | 86 | 87 |

TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION

88 | 89 | 90 |

91 | 0. 92 | This License applies to any program or other work which contains 93 | a notice placed by the copyright holder saying it may be distributed 94 | under the terms of this General Public License. The "Program", below, 95 | refers to any such program or work, and a "work based on the Program" 96 | means either the Program or any derivative work under copyright law: 97 | that is to say, a work containing the Program or a portion of it, 98 | either verbatim or with modifications and/or translated into another 99 | language. (Hereinafter, translation is included without limitation in 100 | the term "modification".) Each licensee is addressed as "you". 101 |

102 | 103 |

104 | Activities other than copying, distribution and modification are not 105 | covered by this License; they are outside its scope. The act of 106 | running the Program is not restricted, and the output from the Program 107 | is covered only if its contents constitute a work based on the 108 | Program (independent of having been made by running the Program). 109 | Whether that is true depends on what the Program does. 110 |

111 | 112 |

113 | 1. 114 | You may copy and distribute verbatim copies of the Program's 115 | source code as you receive it, in any medium, provided that you 116 | conspicuously and appropriately publish on each copy an appropriate 117 | copyright notice and disclaimer of warranty; keep intact all the 118 | notices that refer to this License and to the absence of any warranty; 119 | and give any other recipients of the Program a copy of this License 120 | along with the Program. 121 |

122 | 123 |

124 | You may charge a fee for the physical act of transferring a copy, and 125 | you may at your option offer warranty protection in exchange for a fee. 126 |

127 | 128 |

129 | 2. 130 | You may modify your copy or copies of the Program or any portion 131 | of it, thus forming a work based on the Program, and copy and 132 | distribute such modifications or work under the terms of Section 1 133 | above, provided that you also meet all of these conditions: 134 |

135 | 136 |
137 |
138 |
139 | a) 140 | You must cause the modified files to carry prominent notices 141 | stating that you changed the files and the date of any change. 142 |
143 |
144 |
145 | b) 146 | You must cause any work that you distribute or publish, that in 147 | whole or in part contains or is derived from the Program or any 148 | part thereof, to be licensed as a whole at no charge to all third 149 | parties under the terms of this License. 150 |
151 |
152 |
153 | c) 154 | If the modified program normally reads commands interactively 155 | when run, you must cause it, when started running for such 156 | interactive use in the most ordinary way, to print or display an 157 | announcement including an appropriate copyright notice and a 158 | notice that there is no warranty (or else, saying that you provide 159 | a warranty) and that users may redistribute the program under 160 | these conditions, and telling the user how to view a copy of this 161 | License. (Exception: if the Program itself is interactive but 162 | does not normally print such an announcement, your work based on 163 | the Program is not required to print an announcement.) 164 |
165 |
166 | 167 |

168 | These requirements apply to the modified work as a whole. If 169 | identifiable sections of that work are not derived from the Program, 170 | and can be reasonably considered independent and separate works in 171 | themselves, then this License, and its terms, do not apply to those 172 | sections when you distribute them as separate works. But when you 173 | distribute the same sections as part of a whole which is a work based 174 | on the Program, the distribution of the whole must be on the terms of 175 | this License, whose permissions for other licensees extend to the 176 | entire whole, and thus to each and every part regardless of who wrote it. 177 |

178 | 179 |

180 | Thus, it is not the intent of this section to claim rights or contest 181 | your rights to work written entirely by you; rather, the intent is to 182 | exercise the right to control the distribution of derivative or 183 | collective works based on the Program. 184 |

185 | 186 |

187 | In addition, mere aggregation of another work not based on the Program 188 | with the Program (or with a work based on the Program) on a volume of 189 | a storage or distribution medium does not bring the other work under 190 | the scope of this License. 191 |

192 | 193 |

194 | 3. 195 | You may copy and distribute the Program (or a work based on it, 196 | under Section 2) in object code or executable form under the terms of 197 | Sections 1 and 2 above provided that you also do one of the following: 198 |

199 | 200 | 201 | 202 | 203 |
204 |
205 |
206 | a) 207 | Accompany it with the complete corresponding machine-readable 208 | source code, which must be distributed under the terms of Sections 209 | 1 and 2 above on a medium customarily used for software interchange; or, 210 |
211 |
212 |
213 | b) 214 | Accompany it with a written offer, valid for at least three 215 | years, to give any third party, for a charge no more than your 216 | cost of physically performing source distribution, a complete 217 | machine-readable copy of the corresponding source code, to be 218 | distributed under the terms of Sections 1 and 2 above on a medium 219 | customarily used for software interchange; or, 220 |
221 |
222 |
223 | c) 224 | Accompany it with the information you received as to the offer 225 | to distribute corresponding source code. (This alternative is 226 | allowed only for noncommercial distribution and only if you 227 | received the program in object code or executable form with such 228 | an offer, in accord with Subsection b above.) 229 |
230 |
231 | 232 |

233 | The source code for a work means the preferred form of the work for 234 | making modifications to it. For an executable work, complete source 235 | code means all the source code for all modules it contains, plus any 236 | associated interface definition files, plus the scripts used to 237 | control compilation and installation of the executable. However, as a 238 | special exception, the source code distributed need not include 239 | anything that is normally distributed (in either source or binary 240 | form) with the major components (compiler, kernel, and so on) of the 241 | operating system on which the executable runs, unless that component 242 | itself accompanies the executable. 243 |

244 | 245 |

246 | If distribution of executable or object code is made by offering 247 | access to copy from a designated place, then offering equivalent 248 | access to copy the source code from the same place counts as 249 | distribution of the source code, even though third parties are not 250 | compelled to copy the source along with the object code. 251 |

252 | 253 |

254 | 4. 255 | You may not copy, modify, sublicense, or distribute the Program 256 | except as expressly provided under this License. Any attempt 257 | otherwise to copy, modify, sublicense or distribute the Program is 258 | void, and will automatically terminate your rights under this License. 259 | However, parties who have received copies, or rights, from you under 260 | this License will not have their licenses terminated so long as such 261 | parties remain in full compliance. 262 |

263 | 264 |

265 | 5. 266 | You are not required to accept this License, since you have not 267 | signed it. However, nothing else grants you permission to modify or 268 | distribute the Program or its derivative works. These actions are 269 | prohibited by law if you do not accept this License. Therefore, by 270 | modifying or distributing the Program (or any work based on the 271 | Program), you indicate your acceptance of this License to do so, and 272 | all its terms and conditions for copying, distributing or modifying 273 | the Program or works based on it. 274 |

275 | 276 |

277 | 6. 278 | Each time you redistribute the Program (or any work based on the 279 | Program), the recipient automatically receives a license from the 280 | original licensor to copy, distribute or modify the Program subject to 281 | these terms and conditions. You may not impose any further 282 | restrictions on the recipients' exercise of the rights granted herein. 283 | You are not responsible for enforcing compliance by third parties to 284 | this License. 285 |

286 | 287 |

288 | 7. 289 | If, as a consequence of a court judgment or allegation of patent 290 | infringement or for any other reason (not limited to patent issues), 291 | conditions are imposed on you (whether by court order, agreement or 292 | otherwise) that contradict the conditions of this License, they do not 293 | excuse you from the conditions of this License. If you cannot 294 | distribute so as to satisfy simultaneously your obligations under this 295 | License and any other pertinent obligations, then as a consequence you 296 | may not distribute the Program at all. For example, if a patent 297 | license would not permit royalty-free redistribution of the Program by 298 | all those who receive copies directly or indirectly through you, then 299 | the only way you could satisfy both it and this License would be to 300 | refrain entirely from distribution of the Program. 301 |

302 | 303 |

304 | If any portion of this section is held invalid or unenforceable under 305 | any particular circumstance, the balance of the section is intended to 306 | apply and the section as a whole is intended to apply in other 307 | circumstances. 308 |

309 | 310 |

311 | It is not the purpose of this section to induce you to infringe any 312 | patents or other property right claims or to contest validity of any 313 | such claims; this section has the sole purpose of protecting the 314 | integrity of the free software distribution system, which is 315 | implemented by public license practices. Many people have made 316 | generous contributions to the wide range of software distributed 317 | through that system in reliance on consistent application of that 318 | system; it is up to the author/donor to decide if he or she is willing 319 | to distribute software through any other system and a licensee cannot 320 | impose that choice. 321 |

322 | 323 |

324 | This section is intended to make thoroughly clear what is believed to 325 | be a consequence of the rest of this License. 326 |

327 | 328 |

329 | 8. 330 | If the distribution and/or use of the Program is restricted in 331 | certain countries either by patents or by copyrighted interfaces, the 332 | original copyright holder who places the Program under this License 333 | may add an explicit geographical distribution limitation excluding 334 | those countries, so that distribution is permitted only in or among 335 | countries not thus excluded. In such case, this License incorporates 336 | the limitation as if written in the body of this License. 337 |

338 | 339 |

340 | 9. 341 | The Free Software Foundation may publish revised and/or new versions 342 | of the General Public License from time to time. Such new versions will 343 | be similar in spirit to the present version, but may differ in detail to 344 | address new problems or concerns. 345 |

346 | 347 |

348 | Each version is given a distinguishing version number. If the Program 349 | specifies a version number of this License which applies to it and "any 350 | later version", you have the option of following the terms and conditions 351 | either of that version or of any later version published by the Free 352 | Software Foundation. If the Program does not specify a version number of 353 | this License, you may choose any version ever published by the Free Software 354 | Foundation. 355 |

356 | 357 |

358 | 10. 359 | If you wish to incorporate parts of the Program into other free 360 | programs whose distribution conditions are different, write to the author 361 | to ask for permission. For software which is copyrighted by the Free 362 | Software Foundation, write to the Free Software Foundation; we sometimes 363 | make exceptions for this. Our decision will be guided by the two goals 364 | of preserving the free status of all derivatives of our free software and 365 | of promoting the sharing and reuse of software generally. 366 |

367 | 368 |

NO WARRANTY

369 | 370 |

371 | 11. 372 | BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 373 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 374 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 375 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 376 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 377 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 378 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 379 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 380 | REPAIR OR CORRECTION. 381 |

382 | 383 |

384 | 12. 385 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 386 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 387 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 388 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 389 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 390 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 391 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 392 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 393 | POSSIBILITY OF SUCH DAMAGES. 394 |

395 | 396 |

END OF TERMS AND CONDITIONS

397 | 398 |

How to Apply These Terms to Your New Programs

399 | 400 |

401 | If you develop a new program, and you want it to be of the greatest 402 | possible use to the public, the best way to achieve this is to make it 403 | free software which everyone can redistribute and change under these terms. 404 |

405 | 406 |

407 | To do so, attach the following notices to the program. It is safest 408 | to attach them to the start of each source file to most effectively 409 | convey the exclusion of warranty; and each file should have at least 410 | the "copyright" line and a pointer to where the full notice is found. 411 |

412 | 413 |
one line to give the program's name and an idea of what it does.
414 | Copyright (C) yyyy  name of author
415 | 
416 | This program is free software; you can redistribute it and/or
417 | modify it under the terms of the GNU General Public License
418 | as published by the Free Software Foundation; either version 2
419 | of the License, or (at your option) any later version.
420 | 
421 | This program is distributed in the hope that it will be useful,
422 | but WITHOUT ANY WARRANTY; without even the implied warranty of
423 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
424 | GNU General Public License for more details.
425 | 
426 | You should have received a copy of the GNU General Public License
427 | along with this program; if not, write to the Free Software
428 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
429 | 
430 | 431 |

432 | Also add information on how to contact you by electronic and paper mail. 433 |

434 | 435 |

436 | If the program is interactive, make it output a short notice like this 437 | when it starts in an interactive mode: 438 |

439 | 440 |
Gnomovision version 69, Copyright (C) year name of author
441 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details
442 | type `show w'.  This is free software, and you are welcome
443 | to redistribute it under certain conditions; type `show c' 
444 | for details.
445 | 
446 | 447 |

448 | The hypothetical commands `show w' and `show c' should show 449 | the appropriate parts of the General Public License. Of course, the 450 | commands you use may be called something other than `show w' and 451 | `show c'; they could even be mouse-clicks or menu items--whatever 452 | suits your program. 453 |

454 | 455 |

456 | You should also get your employer (if you work as a programmer) or your 457 | school, if any, to sign a "copyright disclaimer" for the program, if 458 | necessary. Here is a sample; alter the names: 459 |

460 | 461 | 462 |
Yoyodyne, Inc., hereby disclaims all copyright
463 | interest in the program `Gnomovision'
464 | (which makes passes at compilers) written 
465 | by James Hacker.
466 | 
467 | signature of Ty Coon, 1 April 1989
468 | Ty Coon, President of Vice
469 | 
470 | 471 |

472 | This General Public License does not permit incorporating your program into 473 | proprietary programs. If your program is a subroutine library, you may 474 | consider it more useful to permit linking proprietary applications with the 475 | library. If this is what you want to do, use the 476 | GNU Lesser General Public License 477 | instead of this License. 478 |

479 | 480 | 481 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | Double File Scanner 2 | Copyright (C) 2014-2017 LoRd_MuldeR . Some rights reserved. 3 | 4 | This program is free software; you can redistribute it and/or 5 | modify it under the terms of the GNU General Public License 6 | as published by the Free Software Foundation; either version 2 7 | of the License, or (at your option) any later version. 8 | 9 | This program is distributed in the hope that it will be useful, 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | GNU General Public License for more details. 13 | 14 | You should have received a copy of the GNU General Public License 15 | along with this program; if not, write to the Free Software 16 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 | 18 | Please check http://muldersoft.com/ for news and updates! 19 | 20 | 21 | ------------------------------------------------------------------------------ 22 | 1. Introduction 23 | ------------------------------------------------------------------------------ 24 | 25 | The purpose of this tool is scanning the selected directory or directories for 26 | duplicate files, i.e. files with identical content. Duplicate files are 27 | identified by first calculating the SHA-1 digest of each file and then looking 28 | for values that appear more than once. In particular, files with identical 29 | content are guaranteed to have the same SHA-1 digest, while files with 30 | differing content will have different SHA-1 values with very high certainty. 31 | 32 | All computed SHA-1 values are stored in a hash table, so collisions are found 33 | quickly and we do NOT need to compare every digest to every other one. Also, 34 | the files are processed concurrently in multiple "worker" threads in order to 35 | parallelize and speed-up the SHA-1 computations on multi-core processors. On 36 | our test machine it took ~15 minutes to analyse all the ~260,000 files on the 37 | system drive (~63.5 GB). During this operation ~44,000 duplicates were found. 38 | 39 | Once the scan is completed, the program provides commands to review, rename or 40 | delete the duplicate files manually. Alternatively, the "automatic clean-up" 41 | wizard may be invoked in order to remove ALL redundant files at a single blow. 42 | Optionally, the list of duplicates can be exported to the XML or INI format. 43 | 44 | 45 | ------------------------------------------------------------------------------ 46 | 2. Platform Support 47 | ------------------------------------------------------------------------------ 48 | 49 | The following platforms are supported: 50 | - Windows 10, 32-Bit and 64-Bit versions 51 | - Windows 8 and 8.1, 32-Bit and 64-Bit versions 52 | - Windows 7, 32-Bit and 64-Bit versions 53 | 54 | Platforms that should work too, but are not tested extensively: 55 | - Windows Vista, 32-Bit and 64-Bit versions 56 | - Windows XP, Service Pack 3 is required! 57 | - Windows XP Professional x64 Edition, Service Pack 2 is required! 58 | 59 | 60 | ------------------------------------------------------------------------------ 61 | 3. Anti-Virus Warning 62 | ------------------------------------------------------------------------------ 63 | 64 | Anti-Virus programs can interfere with the Double File Scanner software and 65 | significantly slow down the process! Therefore it is highly recommend to turn 66 | off the "real time scanner" or "guard" feature of your Anti-Virus program 67 | while the Double File Scanner is running. But don't forget to re-enable it! 68 | 69 | 70 | ------------------------------------------------------------------------------ 71 | 4. Command-Line Options 72 | ------------------------------------------------------------------------------ 73 | 74 | The following command-line options are available: 75 | --console Enable the debug console 76 | --scan Scan the specified directory, can be used multiple times 77 | 78 | List of influential environment variables: 79 | DBLSCAN_THREADS Set the number of worker threads (default: auto detect) 80 | 81 | 82 | ------------------------------------------------------------------------------ 83 | 5. Source Code 84 | ------------------------------------------------------------------------------ 85 | 86 | Find the Double File Scanner source code at our official Git mirrors: 87 | - https://github.com/lordmulder/DoubleFilerScanner 88 | - https://gitlab.com/double-file-scanner/double-file-scanner 89 | - https://bitbucket.org/muldersoft/doubefilescanner 90 | - https://app.assembla.com/spaces/doublefilerscanner/ 91 | 92 | More free software available at MuldeR's OpenSource Projects web-site: 93 | http://muldersoft.com/ 94 | 95 | 96 | ------------------------------------------------------------------------------ 97 | 6. Frequently Asked Question 98 | ------------------------------------------------------------------------------ 99 | 100 | Q: Can the scanning process be aborted? 101 | A: Yes, simply press the 'ESC' key! 102 | 103 | Q: Can the scanning process be suspended/resumed? 104 | A: Yes, simply press the 'Pause' key! 105 | 106 | 107 | E.O.F. 108 | -------------------------------------------------------------------------------- /etc/Manifest/Compatibility.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /etc/inifile.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lordmulder/DoubleFilerScanner/58c47313ac65bbcd6d99422c20922cd847cad688/etc/inifile.exe -------------------------------------------------------------------------------- /gui/Window_Directories.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | DirectoriesDialog 4 | 5 | 6 | Qt::ApplicationModal 7 | 8 | 9 | 10 | 0 11 | 0 12 | 594 13 | 240 14 | 15 | 16 | 17 | Select Directories 18 | 19 | 20 | 21 | 12 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 150 35 | 0 36 | 37 | 38 | 39 | Add Directory 40 | 41 | 42 | 43 | :/res/Button_FolderAdd.png:/res/Button_FolderAdd.png 44 | 45 | 46 | 47 | 48 | 49 | 50 | Qt::Horizontal 51 | 52 | 53 | 54 | 40 55 | 20 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 150 65 | 0 66 | 67 | 68 | 69 | Remove Directory 70 | 71 | 72 | 73 | :/res/Button_FolderRemove.png:/res/Button_FolderRemove.png 74 | 75 | 76 | 77 | 78 | 79 | 80 | Qt::Horizontal 81 | 82 | 83 | 84 | 40 85 | 20 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 150 95 | 0 96 | 97 | 98 | 99 | Clear Directories 100 | 101 | 102 | 103 | :/res/Button_Clear.png:/res/Button_Clear.png 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | QFrame::HLine 113 | 114 | 115 | QFrame::Sunken 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | Recurse Sub-Directories 125 | 126 | 127 | true 128 | 129 | 130 | true 131 | 132 | 133 | 134 | 135 | 136 | 137 | Qt::Horizontal 138 | 139 | 140 | 141 | 40 142 | 20 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 90 152 | 0 153 | 154 | 155 | 156 | OK 157 | 158 | 159 | 160 | :/res/Button_Accept.png:/res/Button_Accept.png 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 90 169 | 0 170 | 171 | 172 | 173 | Cancel 174 | 175 | 176 | 177 | :/res/Button_Cancel.png:/res/Button_Cancel.png 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | buttonOkay 189 | buttonCancel 190 | buttonAddDir 191 | buttonRemoveDir 192 | buttonClear 193 | listWidget 194 | checkBoxRecursive 195 | 196 | 197 | 198 | 199 | 200 | 201 | buttonOkay 202 | clicked() 203 | DirectoriesDialog 204 | accept() 205 | 206 | 207 | 441 208 | 350 209 | 210 | 211 | 296 212 | 186 213 | 214 | 215 | 216 | 217 | buttonCancel 218 | clicked() 219 | DirectoriesDialog 220 | reject() 221 | 222 | 223 | 537 224 | 350 225 | 226 | 227 | 296 228 | 186 229 | 230 | 231 | 232 | 233 | 234 | -------------------------------------------------------------------------------- /gui/Window_Main.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 800 10 | 420 11 | 12 | 13 | 14 | 15 | 512 16 | 384 17 | 18 | 19 | 20 | Double File Scanner 21 | 22 | 23 | 24 | :/res/DoubleFileScanner.png:/res/DoubleFileScanner.png 25 | 26 | 27 | 28 | 29 | 12 30 | 31 | 32 | 33 | 34 | 6 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 12 44 | 45 | 46 | 47 | 48 | :/res/Logo.png 49 | 50 | 51 | 52 | 53 | 54 | 55 | 3 56 | 57 | 58 | 59 | 60 | Operation not started yet. Please click "Start Scan" in order to begin! 61 | 62 | 63 | 64 | 65 | 66 | 67 | 0 68 | 69 | 70 | Qt::AlignCenter 71 | 72 | 73 | true 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | true 86 | 87 | 88 | false 89 | 90 | 91 | 92 | 93 | 94 | 95 | QFrame::HLine 96 | 97 | 98 | QFrame::Sunken 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 120 109 | 0 110 | 111 | 112 | 113 | PointingHandCursor 114 | 115 | 116 | Start Scan 117 | 118 | 119 | 120 | :/res/Button_Start.png:/res/Button_Start.png 121 | 122 | 123 | 124 | 125 | 126 | 127 | Qt::Horizontal 128 | 129 | 130 | 131 | 40 132 | 20 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 120 142 | 0 143 | 144 | 145 | 146 | PointingHandCursor 147 | 148 | 149 | About... 150 | 151 | 152 | 153 | :/res/Button_About.png:/res/Button_About.png 154 | 155 | 156 | 157 | 158 | 159 | 160 | Qt::Horizontal 161 | 162 | 163 | 164 | 40 165 | 20 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 120 175 | 0 176 | 177 | 178 | 179 | PointingHandCursor 180 | 181 | 182 | Exit Program 183 | 184 | 185 | 186 | :/res/Button_Exit.png:/res/Button_Exit.png 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 0 200 | 0 201 | 800 202 | 21 203 | 204 | 205 | 206 | 207 | File 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | Edit 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | ? 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | :/res/Button_Start.png:/res/Button_Start.png 244 | 245 | 246 | Start New Scan 247 | 248 | 249 | 250 | 251 | 252 | :/res/Button_Clear.png:/res/Button_Clear.png 253 | 254 | 255 | Clear 256 | 257 | 258 | 259 | 260 | 261 | :/res/Button_Exit.png:/res/Button_Exit.png 262 | 263 | 264 | Exit Program 265 | 266 | 267 | 268 | 269 | 270 | :/res/Button_Export.png:/res/Button_Export.png 271 | 272 | 273 | Export Duplicates List 274 | 275 | 276 | 277 | 278 | 279 | :/res/Button_About.png:/res/Button_About.png 280 | 281 | 282 | About... 283 | 284 | 285 | 286 | 287 | 288 | :/res/Button_CopyToClipboard.png:/res/Button_CopyToClipboard.png 289 | 290 | 291 | Copy to Clipboard 292 | 293 | 294 | 295 | 296 | 297 | :/res/Button_WebLink.png:/res/Button_WebLink.png 298 | 299 | 300 | Official Web-Site 301 | 302 | 303 | 304 | 305 | 306 | :/res/Button_FileGoto.png:/res/Button_FileGoto.png 307 | 308 | 309 | Go to Selected File 310 | 311 | 312 | 313 | 314 | 315 | :/res/Button_FileDelete.png:/res/Button_FileDelete.png 316 | 317 | 318 | Delete Selected File 319 | 320 | 321 | 322 | 323 | 324 | :/res/Button_FileRename.png:/res/Button_FileRename.png 325 | 326 | 327 | Rename Selected File 328 | 329 | 330 | 331 | 332 | 333 | :/res/Button_FileOpen.png:/res/Button_FileOpen.png 334 | 335 | 336 | Open Selected File 337 | 338 | 339 | 340 | 341 | 342 | :/res/Button_AutoClean.png:/res/Button_AutoClean.png 343 | 344 | 345 | Automatic Clean-Up 346 | 347 | 348 | 349 | 350 | buttonStart 351 | buttonAbout 352 | buttonExit 353 | treeView 354 | 355 | 356 | 357 | 358 | 359 | 360 | -------------------------------------------------------------------------------- /res/Button_About.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lordmulder/DoubleFilerScanner/58c47313ac65bbcd6d99422c20922cd847cad688/res/Button_About.png -------------------------------------------------------------------------------- /res/Button_Accept.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lordmulder/DoubleFilerScanner/58c47313ac65bbcd6d99422c20922cd847cad688/res/Button_Accept.png -------------------------------------------------------------------------------- /res/Button_AutoClean.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lordmulder/DoubleFilerScanner/58c47313ac65bbcd6d99422c20922cd847cad688/res/Button_AutoClean.png -------------------------------------------------------------------------------- /res/Button_Cancel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lordmulder/DoubleFilerScanner/58c47313ac65bbcd6d99422c20922cd847cad688/res/Button_Cancel.png -------------------------------------------------------------------------------- /res/Button_Clear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lordmulder/DoubleFilerScanner/58c47313ac65bbcd6d99422c20922cd847cad688/res/Button_Clear.png -------------------------------------------------------------------------------- /res/Button_CopyToClipboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lordmulder/DoubleFilerScanner/58c47313ac65bbcd6d99422c20922cd847cad688/res/Button_CopyToClipboard.png -------------------------------------------------------------------------------- /res/Button_Exit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lordmulder/DoubleFilerScanner/58c47313ac65bbcd6d99422c20922cd847cad688/res/Button_Exit.png -------------------------------------------------------------------------------- /res/Button_Export.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lordmulder/DoubleFilerScanner/58c47313ac65bbcd6d99422c20922cd847cad688/res/Button_Export.png -------------------------------------------------------------------------------- /res/Button_FileDelete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lordmulder/DoubleFilerScanner/58c47313ac65bbcd6d99422c20922cd847cad688/res/Button_FileDelete.png -------------------------------------------------------------------------------- /res/Button_FileGoto.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lordmulder/DoubleFilerScanner/58c47313ac65bbcd6d99422c20922cd847cad688/res/Button_FileGoto.png -------------------------------------------------------------------------------- /res/Button_FileOpen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lordmulder/DoubleFilerScanner/58c47313ac65bbcd6d99422c20922cd847cad688/res/Button_FileOpen.png -------------------------------------------------------------------------------- /res/Button_FileRename.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lordmulder/DoubleFilerScanner/58c47313ac65bbcd6d99422c20922cd847cad688/res/Button_FileRename.png -------------------------------------------------------------------------------- /res/Button_FolderAdd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lordmulder/DoubleFilerScanner/58c47313ac65bbcd6d99422c20922cd847cad688/res/Button_FolderAdd.png -------------------------------------------------------------------------------- /res/Button_FolderRemove.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lordmulder/DoubleFilerScanner/58c47313ac65bbcd6d99422c20922cd847cad688/res/Button_FolderRemove.png -------------------------------------------------------------------------------- /res/Button_Start.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lordmulder/DoubleFilerScanner/58c47313ac65bbcd6d99422c20922cd847cad688/res/Button_Start.png -------------------------------------------------------------------------------- /res/Button_WebLink.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lordmulder/DoubleFilerScanner/58c47313ac65bbcd6d99422c20922cd847cad688/res/Button_WebLink.png -------------------------------------------------------------------------------- /res/DoubleFileScanner.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lordmulder/DoubleFilerScanner/58c47313ac65bbcd6d99422c20922cd847cad688/res/DoubleFileScanner.ico -------------------------------------------------------------------------------- /res/DoubleFileScanner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lordmulder/DoubleFilerScanner/58c47313ac65bbcd6d99422c20922cd847cad688/res/DoubleFileScanner.png -------------------------------------------------------------------------------- /res/Icon_Bullet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lordmulder/DoubleFilerScanner/58c47313ac65bbcd6d99422c20922cd847cad688/res/Icon_Bullet.png -------------------------------------------------------------------------------- /res/Icon_Duplicate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lordmulder/DoubleFilerScanner/58c47313ac65bbcd6d99422c20922cd847cad688/res/Icon_Duplicate.png -------------------------------------------------------------------------------- /res/Icon_Folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lordmulder/DoubleFilerScanner/58c47313ac65bbcd6d99422c20922cd847cad688/res/Icon_Folder.png -------------------------------------------------------------------------------- /res/Logo.old: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lordmulder/DoubleFilerScanner/58c47313ac65bbcd6d99422c20922cd847cad688/res/Logo.old -------------------------------------------------------------------------------- /res/Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lordmulder/DoubleFilerScanner/58c47313ac65bbcd6d99422c20922cd847cad688/res/Logo.png -------------------------------------------------------------------------------- /res/Sign_Accept.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lordmulder/DoubleFilerScanner/58c47313ac65bbcd6d99422c20922cd847cad688/res/Sign_Accept.png -------------------------------------------------------------------------------- /res/Sign_Cancel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lordmulder/DoubleFilerScanner/58c47313ac65bbcd6d99422c20922cd847cad688/res/Sign_Cancel.png -------------------------------------------------------------------------------- /res/Sign_Clocks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lordmulder/DoubleFilerScanner/58c47313ac65bbcd6d99422c20922cd847cad688/res/Sign_Clocks.png -------------------------------------------------------------------------------- /res/Spinner.GIF: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lordmulder/DoubleFilerScanner/58c47313ac65bbcd6d99422c20922cd847cad688/res/Spinner.GIF -------------------------------------------------------------------------------- /res/Spinner.new: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lordmulder/DoubleFilerScanner/58c47313ac65bbcd6d99422c20922cd847cad688/res/Spinner.new -------------------------------------------------------------------------------- /src/Config.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Double File Scanner 3 | // Copyright (C) 2014-2017 LoRd_MuldeR 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version, but always including the *additional* 9 | // restrictions defined in the "License.txt" file. 10 | // 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License along 17 | // with this program; if not, write to the Free Software Foundation, Inc., 18 | // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | // 20 | // http://www.gnu.org/licenses/gpl-2.0.txt 21 | /////////////////////////////////////////////////////////////////////////////// 22 | 23 | #include "Config.h" 24 | 25 | #define DBLSCAN_VERSION 26 | #include "Version.h" 27 | 28 | //Version info 29 | const unsigned int DOUBLESCANNER_VERSION_MAJOR = DBLSCAN_VER_MAJOR; 30 | const unsigned int DOUBLESCANNER_VERSION_MINOR = (DBLSCAN_VER_MINOR_HI * 10) + (DBLSCAN_VER_MAJOR_LO % 10); 31 | const unsigned int DOUBLESCANNER_VERSION_PATCH = DBLSCAN_VER_PATCH; 32 | 33 | //Build date/time 34 | const char* DOUBLESCANNER_BUILD_DATE = __DATE__; 35 | const char* DOUBLESCANNER_BUILD_TIME = __TIME__; 36 | 37 | //Compiler detection 38 | #if defined(__INTEL_COMPILER) 39 | #if (__INTEL_COMPILER >= 1300) 40 | static char *DOUBLESCANNER_COMPILER = "ICL 13." LAMEXP_MAKE_STR(__INTEL_COMPILER_BUILD_DATE); 41 | #elif (__INTEL_COMPILER >= 1200) 42 | static char *DOUBLESCANNER_COMPILER = "ICL 12." LAMEXP_MAKE_STR(__INTEL_COMPILER_BUILD_DATE); 43 | #elif (__INTEL_COMPILER >= 1100) 44 | static char *DOUBLESCANNER_COMPILER = "ICL 11.x"; 45 | #elif (__INTEL_COMPILER >= 1000) 46 | static char *DOUBLESCANNER_COMPILER = "ICL 10.x"; 47 | #else 48 | #error Compiler is not supported! 49 | #endif 50 | #elif defined(_MSC_VER) 51 | #if (_MSC_VER == 1900) 52 | #if (_MSC_FULL_VER >= 190024210) && (_MSC_FULL_VER <= 190024215) 53 | const char *DOUBLESCANNER_COMPILER = "MSVC 2015.3"; 54 | #else 55 | #error Compiler version is not supported yet! 56 | #endif 57 | #elif (_MSC_VER == 1800) 58 | #if (_MSC_FULL_VER < 180021005) 59 | const char *DOUBLESCANNER_COMPILER = "MSVC 2013-Beta"; 60 | #elif (_MSC_FULL_VER < 180030501) 61 | const char *DOUBLESCANNER_COMPILER = "MSVC 2013"; 62 | #elif (_MSC_FULL_VER == 180030501) 63 | const char *DOUBLESCANNER_COMPILER = "MSVC 2013.2"; 64 | #else 65 | #error Compiler version is not supported yet! 66 | #endif 67 | #elif (_MSC_VER == 1700) 68 | #if (_MSC_FULL_VER < 170050727) 69 | const char *DOUBLESCANNER_COMPILER = "MSVC 2012-Beta"; 70 | #elif (_MSC_FULL_VER < 170051020) 71 | const char *DOUBLESCANNER_COMPILER = "MSVC 2012"; 72 | #elif (_MSC_FULL_VER < 170051106) 73 | const char *DOUBLESCANNER_COMPILER = "MSVC 2012.1-CTP"; 74 | #elif (_MSC_FULL_VER < 170060315) 75 | const char *DOUBLESCANNER_COMPILER = "MSVC 2012.1"; 76 | #elif (_MSC_FULL_VER < 170060610) 77 | const char *DOUBLESCANNER_COMPILER = "MSVC 2012.2"; 78 | #elif (_MSC_FULL_VER < 170061030) 79 | const char *DOUBLESCANNER_COMPILER = "MSVC 2012.3"; 80 | #elif (_MSC_FULL_VER == 170061030) 81 | const char *DOUBLESCANNER_COMPILER = "MSVC 2012.4"; 82 | #else 83 | #error Compiler version is not supported yet! 84 | #endif 85 | #elif (_MSC_VER == 1600) 86 | #if (_MSC_FULL_VER < 160040219) 87 | const char *DOUBLESCANNER_COMPILER = "MSVC 2010"; 88 | #elif (_MSC_FULL_VER == 160040219) 89 | const char *DOUBLESCANNER_COMPILER = "MSVC 2010-SP1"; 90 | #else 91 | #error Compiler version is not supported yet! 92 | #endif 93 | #elif (_MSC_VER == 1500) 94 | #if (_MSC_FULL_VER >= 150030729) 95 | const char *DOUBLESCANNER_COMPILER = "MSVC 2008-SP1"; 96 | #else 97 | const char *DOUBLESCANNER_COMPILER = "MSVC 2008"; 98 | #endif 99 | #else 100 | #error Compiler is not supported! 101 | #endif 102 | 103 | // Note: /arch:SSE and /arch:SSE2 are only available for the x86 platform 104 | #if !defined(_M_X64) && defined(_M_IX86_FP) 105 | #if (_M_IX86_FP == 1) 106 | #error SSE instruction set is enabled! 107 | #elif (_M_IX86_FP == 2) 108 | #error SSE2 (or higher) instruction set is enabled! 109 | #endif 110 | #endif 111 | #else 112 | #error Compiler is not supported! 113 | #endif 114 | 115 | //Architecture detection 116 | #if defined(_M_X64) 117 | const char *DOUBLESCANNER_ARCH = "x64"; 118 | #elif defined(_M_IX86) 119 | const char *DOUBLESCANNER_ARCH = "x86"; 120 | #else 121 | #error Architecture is not supported! 122 | #endif 123 | -------------------------------------------------------------------------------- /src/Config.h: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Double File Scanner 3 | // Copyright (C) 2014-2017 LoRd_MuldeR 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version, but always including the *additional* 9 | // restrictions defined in the "License.txt" file. 10 | // 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License along 17 | // with this program; if not, write to the Free Software Foundation, Inc., 18 | // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | // 20 | // http://www.gnu.org/licenses/gpl-2.0.txt 21 | /////////////////////////////////////////////////////////////////////////////// 22 | 23 | #pragma once 24 | 25 | //============================================================================= 26 | // Version 27 | //============================================================================= 28 | 29 | //Version info 30 | extern const unsigned int DOUBLESCANNER_VERSION_MAJOR; 31 | extern const unsigned int DOUBLESCANNER_VERSION_MINOR; 32 | extern const unsigned int DOUBLESCANNER_VERSION_PATCH; 33 | 34 | //Build date/time 35 | extern const char* DOUBLESCANNER_BUILD_DATE; 36 | extern const char* DOUBLESCANNER_BUILD_TIME; 37 | 38 | //Compiler info 39 | extern const char* DOUBLESCANNER_COMPILER; 40 | extern const char* DOUBLESCANNER_ARCH; 41 | 42 | //Check for debug build 43 | #if defined(_DEBUG) && defined(QT_DEBUG) && !defined(NDEBUG) && !defined(QT_NO_DEBUG) 44 | #define DOUBLESCANNER_DEBUG (1) 45 | #elif defined(NDEBUG) && defined(QT_NO_DEBUG) && !defined(_DEBUG) && !defined(QT_DEBUG) 46 | #define DOUBLESCANNER_DEBUG (0) 47 | #else 48 | #error Inconsistent debug defines detected! 49 | #endif 50 | 51 | //============================================================================= 52 | // Helper Macros 53 | //============================================================================= 54 | 55 | #define MY_DELETE(X) do \ 56 | { \ 57 | if((X)) \ 58 | { \ 59 | delete (X); \ 60 | (X) = NULL; \ 61 | } \ 62 | } \ 63 | while(0) 64 | -------------------------------------------------------------------------------- /src/Main.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Double File Scanner 3 | // Copyright (C) 2014-2017 LoRd_MuldeR 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version, but always including the *additional* 9 | // restrictions defined in the "License.txt" file. 10 | // 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License along 17 | // with this program; if not, write to the Free Software Foundation, Inc., 18 | // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | // 20 | // http://www.gnu.org/licenses/gpl-2.0.txt 21 | /////////////////////////////////////////////////////////////////////////////// 22 | 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | #include "Config.h" 34 | #include "System.h" 35 | #include "Taskbar.h" 36 | #include "Window_Main.h" 37 | 38 | //Initialize static Qt plugins 39 | #ifdef QT_NODLL 40 | Q_IMPORT_PLUGIN(qico) 41 | Q_IMPORT_PLUGIN(qsvg) 42 | Q_IMPORT_PLUGIN(qtga) 43 | #endif 44 | 45 | //VLD support 46 | #include 47 | 48 | //Message handler routine 49 | static void msg_handler(QtMsgType type, const char *msg) 50 | { 51 | switch(type) 52 | { 53 | case QtWarningMsg: 54 | printConsole(msg, 1); 55 | break; 56 | case QtCriticalMsg: 57 | printConsole(msg, 2); 58 | break; 59 | case QtFatalMsg: 60 | printConsole(msg, 2); 61 | if(!DOUBLESCANNER_DEBUG) 62 | { 63 | crashHandler(msg); 64 | } 65 | break; 66 | default: 67 | printConsole(msg, 0); 68 | break; 69 | } 70 | } 71 | 72 | //Initialize Qt runtime 73 | static QApplication *init_qt(int argc, char* argv[]) 74 | { 75 | qDebug("Using Qt v%s-%s [%s], compiled with Qt v%s [%s]\n", qVersion(), (qSharedBuild() ? "DLL" : "Static"), QLibraryInfo::buildDate().toString(Qt::ISODate).toLatin1().constData(), QT_VERSION_STR, QT_PACKAGEDATE_STR); 76 | 77 | //Create QApplication 78 | QApplication *application = new QApplication(argc, argv); 79 | 80 | //Setup library path 81 | QApplication::setLibraryPaths(QStringList() << QApplication::applicationDirPath()); 82 | qDebug("Library Path:\n%s\n", QApplication::libraryPaths().first().toUtf8().constData()); 83 | 84 | //Check image formats 85 | QList supportedFormats = QImageReader::supportedImageFormats(); 86 | static const char *imageformats[] = {"bmp", "png", "jpg", "gif", "ico", "xpm", NULL}; 87 | for(int i = 0; imageformats[i]; i++) 88 | { 89 | if(!supportedFormats.contains(imageformats[i])) 90 | { 91 | crashHandler("Qt initialization error: Failed to load image format plugins!"); 92 | } 93 | } 94 | 95 | //Setup application 96 | application->setWindowIcon(QIcon(":/res/DoubleFileScanner.png")); 97 | application->setStyle(new QPlastiqueStyle()); 98 | 99 | return application; 100 | } 101 | 102 | //Main function 103 | static int double_file_scanner(int argc, char* argv[]) 104 | { 105 | qInstallMsgHandler(msg_handler); 106 | QTextCodec::setCodecForCStrings(QTextCodec::codecForName("utf-8")); 107 | 108 | qDebug("Double File Scanner, Version %u.%02u-%u", DOUBLESCANNER_VERSION_MAJOR, DOUBLESCANNER_VERSION_MINOR, DOUBLESCANNER_VERSION_PATCH); 109 | qDebug("Copyright (C) 2014-2017 LoRd_MuldeR . Some rights reserved."); 110 | qDebug("Built on %s at %s with %s for Win-%s.\n", DOUBLESCANNER_BUILD_DATE, DOUBLESCANNER_BUILD_TIME, DOUBLESCANNER_COMPILER, DOUBLESCANNER_ARCH); 111 | 112 | qDebug("This program is free software: you can redistribute it and/or modify"); 113 | qDebug("it under the terms of the GNU General Public License ."); 114 | qDebug("Note that this program is distributed with ABSOLUTELY NO WARRANTY.\n"); 115 | 116 | //Create application 117 | QApplication *application = init_qt(argc, argv); 118 | 119 | //Initialize taskbar 120 | Taskbar::init(); 121 | 122 | //Create main window 123 | MainWindow *mainWindow = new MainWindow(); 124 | mainWindow->show(); 125 | 126 | //Begin event processing 127 | application->exec(); 128 | 129 | //Free memory 130 | delete mainWindow; 131 | delete application; 132 | 133 | //Uninitialize taskbar 134 | Taskbar::uninit(); 135 | 136 | return EXIT_SUCCESS; 137 | } 138 | 139 | int main_ex(int argc, char* argv[]) 140 | { 141 | try 142 | { 143 | for(int i = 1; i < argc; i++) 144 | { 145 | if(_stricmp(argv[i], "--console") == 0) 146 | { 147 | initConsole(); 148 | break; 149 | } 150 | } 151 | 152 | return double_file_scanner(argc, argv); 153 | } 154 | catch(std::exception &e) 155 | { 156 | fprintf(stderr, "\nGURU MEDITATION !!!\n\nUnhandeled C++ exception error: %s\n\n", e.what()); 157 | crashHandler(e.what()); 158 | } 159 | catch(...) 160 | { 161 | fprintf(stderr, "\nGURU MEDITATION !!!\n\nUnhandeled unknown C++ exception error!\n\n"); 162 | crashHandler("Unhandeled unknown C++ exception, application will exit!"); 163 | } 164 | return EXIT_FAILURE; 165 | } 166 | 167 | int main(int argc, char* argv[]) 168 | { 169 | if(DOUBLESCANNER_DEBUG) 170 | { 171 | initConsole(); 172 | return double_file_scanner(argc, argv); 173 | } 174 | else 175 | { 176 | __try 177 | { 178 | initErrorHandlers(); 179 | return main_ex(argc, argv); 180 | } 181 | __except(1) 182 | { 183 | fprintf(stderr, "\nGURU MEDITATION !!!\n\nUnhandeled structured exception error!\n\n"); 184 | crashHandler("Unhandeled structured exception, application will exit!"); 185 | } 186 | } 187 | //return EXIT_FAILURE; 188 | } 189 | -------------------------------------------------------------------------------- /src/Model_Duplicates.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Double File Scanner 3 | // Copyright (C) 2014-2017 LoRd_MuldeR 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version, but always including the *additional* 9 | // restrictions defined in the "License.txt" file. 10 | // 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License along 17 | // with this program; if not, write to the Free Software Foundation, Inc., 18 | // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | // 20 | // http://www.gnu.org/licenses/gpl-2.0.txt 21 | /////////////////////////////////////////////////////////////////////////////// 22 | 23 | #include "Model_Duplicates.h" 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "Config.h" 32 | #include "Utilities.h" 33 | #include "System.h" 34 | 35 | #include 36 | 37 | static const QString EMPTY_STRING; 38 | static const QByteArray EMPTY_BYTEARRAY; 39 | static const qint64 ZERO_SIZE = 0; 40 | 41 | static inline QString LIMIT_STR(const QString &str, const int maxLen) 42 | { 43 | const int len = str.length(); 44 | if(len > maxLen) 45 | { 46 | const int suffix = str.lastIndexOf(L'.'); 47 | return (suffix > 0) ? (str.left(maxLen - (len - suffix)) + QChar(ushort(0x2026)) + str.mid(suffix + 1)) : (str.left(maxLen - 1) + QChar(ushort(0x2026))); 48 | } 49 | return str; 50 | } 51 | 52 | static inline QString FORMAT_NUMBER(qint64 value) 53 | { 54 | QString formated; 55 | while(value > 1000) 56 | { 57 | formated.prepend(QString().sprintf(",%03d", int(value % 1000i64))); 58 | value /= 1000i64; 59 | } 60 | formated.prepend(QString().sprintf("%d", int(value % 1000i64))); 61 | return formated; 62 | } 63 | 64 | //=================================================================== 65 | // Duplicates Items 66 | //=================================================================== 67 | 68 | enum 69 | { 70 | ITEM_ROOT = 0, 71 | ITEM_GROUP = 1, 72 | ITEM_FILE = 2 73 | } 74 | ItemType_t; 75 | 76 | class DuplicateItem 77 | { 78 | public: 79 | DuplicateItem(DuplicateItem *const parent = NULL) 80 | : 81 | m_parent(parent) 82 | { 83 | if(parent) 84 | { 85 | parent->addChild(this); 86 | } 87 | } 88 | 89 | virtual ~DuplicateItem(void) 90 | { 91 | removeAllChilderen(); 92 | } 93 | 94 | inline DuplicateItem *child(const int index) const { return m_childeren[index]; } 95 | inline int childCount(void) const { return m_childeren.count(); } 96 | inline void addChild(DuplicateItem *child) { m_childeren << child; } 97 | inline int row(void) { return m_parent ? m_parent->m_childeren.indexOf(this) : 0; } 98 | inline DuplicateItem *parent(void) const { return m_parent; } 99 | 100 | void removeAllChilderen(void) 101 | { 102 | while(!m_childeren.isEmpty()) 103 | { 104 | DuplicateItem *child = m_childeren.takeLast(); 105 | MY_DELETE(child); 106 | } 107 | } 108 | 109 | void removeChild(DuplicateItem *const child) 110 | { 111 | m_childeren.removeAll(child); 112 | delete child; 113 | } 114 | 115 | virtual int type(void) 116 | { 117 | return ITEM_ROOT; 118 | } 119 | 120 | protected: 121 | DuplicateItem *const m_parent; 122 | QList m_childeren; 123 | 124 | private: 125 | DuplicateItem &operator=(const DuplicateItem&) 126 | { 127 | throw std::runtime_error("Unimplemented"); 128 | } 129 | }; 130 | 131 | class DuplicateItem_Group : public DuplicateItem 132 | { 133 | public: 134 | DuplicateItem_Group(DuplicateItem *const parent, const QByteArray &hash) 135 | : 136 | DuplicateItem(parent), 137 | m_hash(hash) 138 | { 139 | /*nithing to do here*/ 140 | } 141 | 142 | virtual int type(void) 143 | { 144 | return ITEM_GROUP; 145 | } 146 | 147 | inline const QByteArray &getHash(void) const { return m_hash; } 148 | 149 | protected: 150 | const QByteArray m_hash; 151 | }; 152 | 153 | class DuplicateItem_File : public DuplicateItem 154 | { 155 | public: 156 | DuplicateItem_File(DuplicateItem *const parent, const QString &filePath, const qint64 &fileSize) 157 | : 158 | DuplicateItem(parent), 159 | m_fileInfo(filePath), 160 | m_fileSize(fileSize) 161 | { 162 | /*nithing to do here*/ 163 | } 164 | 165 | virtual int type(void) 166 | { 167 | return ITEM_FILE; 168 | } 169 | 170 | inline const QString getName(void) const { return m_fileInfo.fileName(); } 171 | inline const QString getPath(void) const { return m_fileInfo.absolutePath(); } 172 | inline const QString getFilePath(void) const { return m_fileInfo.absoluteFilePath(); } 173 | inline void setFilePath(const QString &filePath) { m_fileInfo.setFile(filePath); } 174 | inline const qint64 &getFileSize(void) const { return m_fileSize; } 175 | 176 | protected: 177 | QFileInfo m_fileInfo; 178 | const qint64 m_fileSize; 179 | }; 180 | 181 | //=================================================================== 182 | // Constructor & Destructor 183 | //=================================================================== 184 | 185 | DuplicatesModel::DuplicatesModel(void) 186 | { 187 | m_iconDflt = new QIcon(":/res/Icon_Bullet.png"); 188 | m_iconDupl = new QIcon(":/res/Icon_Duplicate.png"); 189 | 190 | m_fontDflt = new QFont("Monospace"); 191 | m_fontDflt->setStyleHint(QFont::TypeWriter); 192 | 193 | m_fontBold = new QFont("Monospace"); 194 | m_fontBold->setStyleHint(QFont::TypeWriter); 195 | m_fontBold->setBold(true); 196 | 197 | m_root = new DuplicateItem(); 198 | } 199 | 200 | DuplicatesModel::~DuplicatesModel(void) 201 | { 202 | MY_DELETE(m_root); 203 | MY_DELETE(m_fontDflt); 204 | MY_DELETE(m_fontBold); 205 | MY_DELETE(m_iconDflt); 206 | MY_DELETE(m_iconDupl); 207 | } 208 | 209 | //=================================================================== 210 | // Model Functions 211 | //=================================================================== 212 | 213 | QModelIndex DuplicatesModel::index(int row, int column, const QModelIndex &parent) const 214 | { 215 | DuplicateItem *parentItem = m_root; 216 | if(parent.isValid() && parent.internalPointer()) 217 | { 218 | parentItem = static_cast(parent.internalPointer()); 219 | } 220 | 221 | if(parentItem && (row >= 0) && (row < parentItem->childCount())) 222 | { 223 | return createIndex(row, column, parentItem->child(row)); 224 | } 225 | 226 | return QModelIndex(); 227 | } 228 | 229 | QModelIndex DuplicatesModel::parent(const QModelIndex &index) const 230 | { 231 | DuplicateItem *item = m_root; 232 | if(index.isValid() && index.internalPointer()) 233 | { 234 | item = static_cast(index.internalPointer()); 235 | } 236 | 237 | DuplicateItem *parentItem = item->parent(); 238 | if(parentItem && (parentItem != m_root)) 239 | { 240 | return createIndex(parentItem->row(), 0, parentItem); 241 | } 242 | 243 | return QModelIndex(); 244 | } 245 | 246 | int DuplicatesModel::rowCount(const QModelIndex &parent) const 247 | { 248 | DuplicateItem *item = m_root; 249 | if(parent.isValid() && parent.internalPointer()) 250 | { 251 | item = static_cast(parent.internalPointer()); 252 | } 253 | 254 | return item->childCount(); 255 | } 256 | 257 | int DuplicatesModel::columnCount(const QModelIndex &parent) const 258 | { 259 | return 3; 260 | } 261 | 262 | QVariant DuplicatesModel::data(const QModelIndex &index, int role) const 263 | { 264 | DuplicateItem *item = m_root; 265 | if(index.isValid() && index.internalPointer()) 266 | { 267 | item = static_cast(index.internalPointer()); 268 | } 269 | 270 | switch(role) 271 | { 272 | /* ============= DISPLAY ROLE ============= */ 273 | case Qt::DisplayRole: 274 | if(DuplicateItem_File *file = dynamic_cast(item)) 275 | { 276 | switch(index.column()) 277 | { 278 | case 0: return LIMIT_STR(file->getName(), 32); 279 | case 1: return QDir::toNativeSeparators(file->getPath()); 280 | case 2: return Utilities::sizeToString(file->getFileSize()); 281 | } 282 | } 283 | else if(DuplicateItem_Group *group = dynamic_cast(item)) 284 | { 285 | if(index.column() == 0) 286 | { 287 | return QString().sprintf("%.16s (%c%d)", group->getHash().toHex().constData(), ushort(0xd7), group->childCount()); 288 | } 289 | } 290 | break; 291 | /* ============= TOOL-TIP ROLE ============= */ 292 | case Qt::ToolTipRole: 293 | if(DuplicateItem_File *file = dynamic_cast(item)) 294 | { 295 | return (index.column() < 2) ? QDir::toNativeSeparators(file->getFilePath()) : tr("%1 Bytes").arg(FORMAT_NUMBER(file->getFileSize())); 296 | } 297 | else if(DuplicateItem_Group *group = dynamic_cast(item)) 298 | { 299 | return QString().sprintf("SHA-1 Digest: %s", group->getHash().toHex().constData()); 300 | } 301 | break; 302 | /* ============= DECORATION ROLE ============= */ 303 | case Qt::DecorationRole: 304 | if(index.column() == 0) 305 | { 306 | return (item->type() == ITEM_GROUP) ? (*m_iconDupl) : (*m_iconDflt); 307 | } 308 | break; 309 | /* ============= TEXT-ALIGNMENT ROLE ============= */ 310 | case Qt::TextAlignmentRole: 311 | if(index.column() == 2) 312 | { 313 | return QVariant(Qt::AlignRight | Qt::AlignVCenter); 314 | } 315 | break; 316 | /* ============= FONT ROLE ============= */ 317 | case Qt::FontRole: 318 | return (item->type() == ITEM_GROUP) ? (*m_fontBold) : (*m_fontDflt); 319 | } 320 | 321 | return QVariant(); 322 | } 323 | 324 | QVariant DuplicatesModel::headerData(int section, Qt::Orientation orientation, int role) const 325 | { 326 | if(orientation == Qt::Horizontal) 327 | { 328 | switch(role) 329 | { 330 | case Qt::DisplayRole: 331 | switch(section) 332 | { 333 | case 0: 334 | return tr("File Name"); 335 | case 1: 336 | return tr("Location"); 337 | case 2: 338 | return tr("Size"); 339 | } 340 | } 341 | } 342 | 343 | return QVariant(); 344 | } 345 | 346 | //=================================================================== 347 | // Query Functions 348 | //=================================================================== 349 | 350 | unsigned int DuplicatesModel::duplicateCount(void) const 351 | { 352 | return m_root->childCount(); 353 | } 354 | 355 | unsigned int DuplicatesModel::duplicateFileCount(const QModelIndex &index) const 356 | { 357 | if(index.isValid()) 358 | { 359 | if(DuplicateItem *currentItem = static_cast(index.internalPointer())) 360 | { 361 | if(DuplicateItem_File *currentFile = dynamic_cast(currentItem)) 362 | { 363 | if(DuplicateItem_Group *duplicateGroup = dynamic_cast(currentFile->parent())) 364 | { 365 | return duplicateGroup->childCount(); 366 | } 367 | } 368 | } 369 | } 370 | 371 | return 0; 372 | } 373 | 374 | const QString DuplicatesModel::getFilePath(const QModelIndex &index) const 375 | { 376 | if(index.isValid()) 377 | { 378 | if(DuplicateItem *currentItem = static_cast(index.internalPointer())) 379 | { 380 | if(DuplicateItem_File *currentFile = dynamic_cast(currentItem)) 381 | { 382 | return currentFile->getFilePath(); 383 | } 384 | } 385 | } 386 | 387 | return EMPTY_STRING; 388 | } 389 | 390 | const qint64 &DuplicatesModel::getFileSize(const QModelIndex &index) const 391 | { 392 | if(index.isValid()) 393 | { 394 | if(DuplicateItem *currentItem = static_cast(index.internalPointer())) 395 | { 396 | if(DuplicateItem_File *currentFile = dynamic_cast(currentItem)) 397 | { 398 | return currentFile->getFileSize(); 399 | } 400 | } 401 | } 402 | 403 | return ZERO_SIZE; 404 | } 405 | 406 | const QByteArray &DuplicatesModel::getGroupHash(const QModelIndex &index) const 407 | { 408 | if(index.isValid()) 409 | { 410 | if(DuplicateItem *currentItem = static_cast(index.internalPointer())) 411 | { 412 | if(DuplicateItem_Group *currentGroup = dynamic_cast(currentItem)) 413 | { 414 | return currentGroup->getHash(); 415 | } 416 | } 417 | } 418 | 419 | return EMPTY_BYTEARRAY; 420 | } 421 | 422 | QString DuplicatesModel::toString(void) 423 | { 424 | QStringList lines; 425 | const int hashCount = m_root->childCount(); 426 | 427 | for(int i = 0; i < hashCount; i++) 428 | { 429 | if(DuplicateItem_Group *currentGroup = dynamic_cast(m_root->child(i))) 430 | { 431 | lines << QString::fromLatin1(currentGroup->getHash().toHex().constData()); 432 | const int fileCount = currentGroup->childCount(); 433 | 434 | for(int j = 0; j < fileCount; j++) 435 | { 436 | DuplicateItem_File *currentFile = dynamic_cast(currentGroup->child(j)); 437 | lines << QString("- %1").arg(QDir::toNativeSeparators(currentFile->getFilePath())); 438 | } 439 | 440 | lines << QString(); 441 | } 442 | } 443 | 444 | return lines.join("\r\n"); 445 | } 446 | 447 | //=================================================================== 448 | // Edit Functions 449 | //=================================================================== 450 | 451 | void DuplicatesModel::clear(void) 452 | { 453 | beginResetModel(); 454 | m_root->removeAllChilderen(); 455 | endResetModel(); 456 | } 457 | 458 | void DuplicatesModel::addDuplicate(const QByteArray &hash, const QStringList &files, const qint64 &size) 459 | { 460 | if(!files.isEmpty()) 461 | { 462 | beginInsertRows(QModelIndex(), m_root->childCount(), m_root->childCount()); 463 | DuplicateItem_Group *group = new DuplicateItem_Group(m_root, hash); 464 | for(QStringList::ConstIterator iterFile = files.constBegin(); iterFile != files.constEnd(); iterFile++) 465 | { 466 | new DuplicateItem_File(group, (*iterFile), size); 467 | } 468 | endInsertRows(); 469 | } 470 | } 471 | 472 | bool DuplicatesModel::renameFile(const QModelIndex &index, const QString &newFileName) 473 | { 474 | if(index.isValid()) 475 | { 476 | if(DuplicateItem *currentItem = static_cast(index.internalPointer())) 477 | { 478 | if(DuplicateItem_File *currentFile = dynamic_cast(currentItem)) 479 | { 480 | const QString oldFilePath = currentFile->getFilePath(); 481 | if(QFileInfo(oldFilePath).exists() && QFileInfo(oldFilePath).isFile()) 482 | { 483 | QString newFilePath = QString("%1/%2").arg(QFileInfo(oldFilePath).absolutePath(), newFileName); 484 | if(oldFilePath.compare(newFilePath, Qt::CaseInsensitive) == 0) 485 | { 486 | return true; /*no need to rename*/ 487 | } 488 | if(QFileInfo(newFilePath).exists()) 489 | { 490 | const QString suffix = QFileInfo(newFilePath).suffix(); 491 | const QString baseName = QFileInfo(newFilePath).completeBaseName(); 492 | const QString path = QFileInfo(newFilePath).absolutePath(); 493 | for(int n = 2; QFileInfo(newFilePath).exists(); n++) 494 | { 495 | newFilePath = QString("%1/%2 (%3).%4").arg(path, baseName, QString::number(n), suffix); 496 | if(n > SHRT_MAX) return false; 497 | } 498 | } 499 | if(QFile::rename(oldFilePath, newFilePath)) 500 | { 501 | currentFile->setFilePath(newFilePath); 502 | emit dataChanged(index, index); 503 | return true; 504 | } 505 | } 506 | } 507 | } 508 | } 509 | 510 | return false; 511 | } 512 | 513 | bool DuplicatesModel::deleteFile(const QModelIndex &index) 514 | { 515 | if(index.isValid()) 516 | { 517 | if(DuplicateItem *currentItem = static_cast(index.internalPointer())) 518 | { 519 | if(DuplicateItem_File *currentFile = dynamic_cast(currentItem)) 520 | { 521 | const QString oldFilePath = currentFile->getFilePath(); 522 | bool okay = true; 523 | if(QFileInfo(oldFilePath).exists() && QFileInfo(oldFilePath).isFile()) 524 | { 525 | okay = QFile::remove(currentFile->getFilePath()); 526 | if(!okay) 527 | { 528 | QFile::setPermissions(currentFile->getFilePath(), QFile::ReadUser | QFile::WriteUser); 529 | okay = QFile::remove(currentFile->getFilePath()); 530 | } 531 | } 532 | if(okay) 533 | { 534 | if(DuplicateItem *parentItem = currentFile->parent()) 535 | { 536 | beginRemoveRows(parent(index), index.row(), index.row()); 537 | parentItem->removeChild(currentFile); 538 | endRemoveRows(); 539 | } 540 | } 541 | return okay; 542 | } 543 | } 544 | } 545 | 546 | return false; 547 | } 548 | 549 | //=================================================================== 550 | // Export Functions 551 | //=================================================================== 552 | 553 | bool DuplicatesModel::exportToFile(const QString &outFile, const int &format) 554 | { 555 | switch(format) 556 | { 557 | case FORMAT_INI: 558 | return exportToIni(outFile); 559 | break; 560 | case FORMAT_XML: 561 | return exportToXml(outFile); 562 | break; 563 | } 564 | 565 | return false; 566 | } 567 | 568 | bool DuplicatesModel::exportToIni(const QString &outFile) 569 | { 570 | QSettings settings(outFile, QSettings::IniFormat); 571 | const int groupCount = m_root->childCount(); 572 | 573 | settings.clear(); 574 | 575 | if((!settings.isWritable()) || (settings.status() != QSettings::NoError)) 576 | { 577 | qWarning("Failed to open output file!"); 578 | return false; 579 | } 580 | 581 | settings.setValue("generator", tr("Document created with Double File Scanner v%1").arg(QString().sprintf("%u.%02u-%u", DOUBLESCANNER_VERSION_MAJOR, DOUBLESCANNER_VERSION_MINOR, DOUBLESCANNER_VERSION_PATCH))); 582 | settings.setValue("rights", tr("Copyright (C) 2014-2017 LoRd_MuldeR . Some rights reserved.")); 583 | 584 | for(int i = 0; i < groupCount; i++) 585 | { 586 | if(DuplicateItem_Group *currentGroup = dynamic_cast(m_root->child(i))) 587 | { 588 | settings.beginGroup(currentGroup->getHash().toHex()); 589 | unsigned int counter = 0; 590 | const int fileCount = currentGroup->childCount(); 591 | for(int j = 0; j < fileCount; j++) 592 | { 593 | if(DuplicateItem_File *currentFile = dynamic_cast(currentGroup->child(j))) 594 | { 595 | settings.setValue(QString().sprintf("%08u", counter++), QDir::toNativeSeparators(currentFile->getFilePath())); 596 | } 597 | } 598 | settings.endGroup(); 599 | } 600 | } 601 | 602 | if((!settings.isWritable()) || (settings.status() != QSettings::NoError)) 603 | { 604 | qWarning("Failed to write output file!"); 605 | return false; 606 | } 607 | 608 | return true; 609 | } 610 | 611 | bool DuplicatesModel::exportToXml(const QString &outFile) 612 | { 613 | QFile file(outFile); 614 | 615 | if(!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) 616 | { 617 | qWarning("Failed to open output file!"); 618 | return false; 619 | } 620 | 621 | QXmlStreamWriter stream(&file); 622 | const int groupCount = m_root->childCount(); 623 | 624 | stream.setAutoFormatting(true); 625 | stream.writeStartDocument(); 626 | 627 | QString tmpl(" %1 "); 628 | stream.writeComment(tmpl.arg(tr("====================================================================="))); 629 | stream.writeComment(tmpl.arg(tr("Document created with Double File Scanner v%1").arg(QString().sprintf("%u.%02u-%u", DOUBLESCANNER_VERSION_MAJOR, DOUBLESCANNER_VERSION_MINOR, DOUBLESCANNER_VERSION_PATCH)))); 630 | stream.writeComment(tmpl.arg(tr("Copyright (C) 2014-2017 LoRd_MuldeR . Some rights reserved."))); 631 | stream.writeComment(tmpl.arg(tr("====================================================================="))); 632 | 633 | stream.writeStartElement("Duplicates"); 634 | 635 | for(int i = 0; i < groupCount; i++) 636 | { 637 | if(DuplicateItem_Group *currentGroup = dynamic_cast(m_root->child(i))) 638 | { 639 | stream.writeStartElement("Group"); 640 | stream.writeAttribute("Hash", currentGroup->getHash().toHex()); 641 | const int fileCount = currentGroup->childCount(); 642 | for(int j = 0; j < fileCount; j++) 643 | { 644 | if(DuplicateItem_File *currentFile = dynamic_cast(currentGroup->child(j))) 645 | { 646 | stream.writeStartElement("File"); 647 | stream.writeAttribute("Name", QDir::toNativeSeparators(currentFile->getFilePath())); 648 | stream.writeEndElement(); 649 | } 650 | } 651 | stream.writeEndElement(); 652 | } 653 | } 654 | 655 | stream.writeEndElement(); 656 | 657 | if(stream.hasError()) 658 | { 659 | qWarning("Failed to write output file!"); 660 | file.close(); 661 | return false; 662 | } 663 | 664 | file.close(); 665 | return true; 666 | } 667 | -------------------------------------------------------------------------------- /src/Model_Duplicates.h: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Double File Scanner 3 | // Copyright (C) 2014-2017 LoRd_MuldeR 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version, but always including the *additional* 9 | // restrictions defined in the "License.txt" file. 10 | // 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License along 17 | // with this program; if not, write to the Free Software Foundation, Inc., 18 | // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | // 20 | // http://www.gnu.org/licenses/gpl-2.0.txt 21 | /////////////////////////////////////////////////////////////////////////////// 22 | 23 | #pragma once 24 | 25 | #include 26 | #include 27 | 28 | class DuplicateItem; 29 | class QFile; 30 | 31 | //DuplicatesModel class 32 | class DuplicatesModel: public QAbstractItemModel 33 | { 34 | Q_OBJECT 35 | 36 | public: 37 | DuplicatesModel(void); 38 | virtual ~DuplicatesModel(void); 39 | 40 | //QAbstractItemModel 41 | virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; 42 | virtual QModelIndex parent(const QModelIndex &index) const; 43 | virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; 44 | virtual int columnCount(const QModelIndex &parent = QModelIndex()) const; 45 | virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; 46 | virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; 47 | 48 | //Export formats 49 | typedef enum 50 | { 51 | FORMAT_INI = 0, 52 | FORMAT_XML = 1 53 | } 54 | exportFormat_t; 55 | 56 | unsigned int duplicateCount(void) const; 57 | unsigned int duplicateFileCount(const QModelIndex &index) const; 58 | const QString getFilePath(const QModelIndex &index) const; 59 | const qint64 &getFileSize(const QModelIndex &index) const; 60 | const QByteArray &getGroupHash(const QModelIndex &index) const; 61 | QString toString(void); 62 | 63 | void clear(void); 64 | bool renameFile(const QModelIndex &index, const QString &newFileName); 65 | bool deleteFile(const QModelIndex &index); 66 | 67 | bool exportToFile(const QString &outFile, const int &format); 68 | 69 | public slots: 70 | void addDuplicate(const QByteArray &hash, const QStringList &files, const qint64 &size); 71 | 72 | protected: 73 | DuplicateItem *m_root; 74 | 75 | QIcon *m_iconDflt; 76 | QIcon *m_iconDupl; 77 | QFont *m_fontDflt; 78 | QFont *m_fontBold; 79 | 80 | bool exportToIni(const QString &outFile); 81 | bool exportToXml(const QString &outFile); 82 | }; 83 | -------------------------------------------------------------------------------- /src/Resource.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lordmulder/DoubleFilerScanner/58c47313ac65bbcd6d99422c20922cd847cad688/src/Resource.h -------------------------------------------------------------------------------- /src/System.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Double File Scanner 3 | // Copyright (C) 2014-2017 LoRd_MuldeR 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version, but always including the *additional* 9 | // restrictions defined in the "License.txt" file. 10 | // 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License along 17 | // with this program; if not, write to the Free Software Foundation, Inc., 18 | // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | // 20 | // http://www.gnu.org/licenses/gpl-2.0.txt 21 | /////////////////////////////////////////////////////////////////////////////// 22 | 23 | #include "System.h" 24 | 25 | #include "Config.h" 26 | #include "Resource.h" 27 | 28 | #define WIN32_LEAN_AND_MEAN 29 | #include 30 | #include 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | #include 40 | #include 41 | 42 | typedef BOOL (WINAPI *PSetConsoleIcon)(HICON hIcon); 43 | 44 | //=================================================================== 45 | // CriticalSection Class 46 | //=================================================================== 47 | 48 | /*wrapper for native Win32 critical sections*/ 49 | class CriticalSection 50 | { 51 | public: 52 | inline CriticalSection(void) 53 | { 54 | InitializeCriticalSection(&m_win32criticalSection); 55 | } 56 | 57 | inline ~CriticalSection(void) 58 | { 59 | DeleteCriticalSection(&m_win32criticalSection); 60 | } 61 | 62 | inline void enter(void) 63 | { 64 | EnterCriticalSection(&m_win32criticalSection); 65 | } 66 | 67 | inline bool tryEnter(void) 68 | { 69 | return TryEnterCriticalSection(&m_win32criticalSection); 70 | } 71 | 72 | inline void leave(void) 73 | { 74 | LeaveCriticalSection(&m_win32criticalSection); 75 | } 76 | 77 | protected: 78 | CRITICAL_SECTION m_win32criticalSection; 79 | }; 80 | 81 | /*RAII-style critical section locker*/ 82 | class CSLocker 83 | { 84 | public: 85 | inline CSLocker(CriticalSection &criticalSection) 86 | : 87 | m_criticalSection(criticalSection) 88 | { 89 | m_criticalSection.enter(); 90 | } 91 | 92 | inline ~CSLocker(void) 93 | { 94 | m_criticalSection.leave(); 95 | } 96 | 97 | protected: 98 | CriticalSection &m_criticalSection; 99 | }; 100 | 101 | //=================================================================== 102 | // WinMain entry point 103 | //=================================================================== 104 | 105 | extern "C" 106 | { 107 | int mainCRTStartup(void); 108 | 109 | int win32EntryPoint(void) 110 | { 111 | if(!(DOUBLESCANNER_DEBUG)) 112 | { 113 | BOOL debuggerPresent = TRUE; 114 | if(!CheckRemoteDebuggerPresent(GetCurrentProcess(), &debuggerPresent)) 115 | { 116 | debuggerPresent = FALSE; 117 | } 118 | if(debuggerPresent || IsDebuggerPresent()) 119 | { 120 | MessageBoxW(NULL, L"Not a debug build. Unload debugger and try again!", L"Debugger", MB_TOPMOST | MB_ICONSTOP); 121 | return -1; 122 | } 123 | else 124 | { 125 | return mainCRTStartup(); 126 | } 127 | } 128 | else 129 | { 130 | return mainCRTStartup(); 131 | } 132 | } 133 | } 134 | 135 | //=================================================================== 136 | // Error Handlers 137 | //=================================================================== 138 | 139 | static const DWORD g_mainThread = GetCurrentThreadId(); 140 | static CriticalSection g_crashLock; 141 | static volatile bool g_crashFlag = true; 142 | 143 | static void my_invalid_param_handler(const wchar_t*, const wchar_t*, const wchar_t*, unsigned int, uintptr_t) 144 | { 145 | crashHandler("Invalid parameter handler invoked, application will exit!"); 146 | } 147 | 148 | static void my_signal_handler(int signal_num) 149 | { 150 | signal(signal_num, my_signal_handler); 151 | crashHandler("Signal handler invoked unexpectedly, application will exit!"); 152 | } 153 | 154 | static LONG WINAPI my_exception_handler(struct _EXCEPTION_POINTERS*) 155 | { 156 | crashHandler("Unhandeled exception handler invoked, application will exit!"); 157 | return EXCEPTION_EXECUTE_HANDLER; 158 | } 159 | 160 | void initErrorHandlers() 161 | { 162 | SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX); 163 | SetUnhandledExceptionFilter(my_exception_handler); 164 | SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL); 165 | _set_invalid_parameter_handler(my_invalid_param_handler); 166 | 167 | static const int signal_num[6] = { SIGABRT, SIGFPE, SIGILL, SIGINT, SIGSEGV, SIGTERM }; 168 | 169 | for(size_t i = 0; i < 6; i++) 170 | { 171 | signal(signal_num[i], my_signal_handler); 172 | } 173 | } 174 | 175 | static DWORD WINAPI crashHelper(LPVOID lpParameter) 176 | { 177 | MessageBoxA(NULL, (LPCSTR) lpParameter, "GURU MEDITATION", MB_OK | MB_ICONERROR | MB_TASKMODAL | MB_TOPMOST | MB_SETFOREGROUND); 178 | return 0; 179 | } 180 | 181 | void crashHandler(const char *message) 182 | { 183 | g_crashLock.enter(); 184 | 185 | if(!g_crashFlag) 186 | { 187 | return; /*prevent recursive invocation*/ 188 | } 189 | 190 | g_crashFlag = false; 191 | 192 | if(g_mainThread != GetCurrentThreadId()) 193 | { 194 | if(HANDLE hThreadMain = OpenThread(THREAD_SUSPEND_RESUME, FALSE, g_mainThread)) 195 | { 196 | SuspendThread(hThreadMain); /*stop main thread*/ 197 | } 198 | } 199 | 200 | if(HANDLE hThread = CreateThread(NULL, 0, crashHelper, (LPVOID) message, 0, NULL)) 201 | { 202 | WaitForSingleObject(hThread, INFINITE); 203 | } 204 | 205 | for(;;) 206 | { 207 | TerminateProcess(GetCurrentProcess(), 666); 208 | } 209 | } 210 | 211 | //=================================================================== 212 | // Console Support 213 | //=================================================================== 214 | 215 | static CriticalSection g_consoleLock; 216 | static volatile HANDLE g_hConsole = INVALID_HANDLE_VALUE; 217 | 218 | void initConsole(void) 219 | { 220 | CSLocker csLocker(g_consoleLock); 221 | 222 | if((g_hConsole != NULL) && (g_hConsole != INVALID_HANDLE_VALUE)) 223 | { 224 | return; /*console already initialized*/ 225 | } 226 | 227 | if(AllocConsole()) 228 | { 229 | g_hConsole = GetStdHandle(STD_OUTPUT_HANDLE); 230 | 231 | SetConsoleOutputCP(CP_UTF8); 232 | SetConsoleTitleW(L"Double File Scanner"); 233 | SetConsoleCtrlHandler(NULL, TRUE); 234 | 235 | const int flags = _O_WRONLY | _O_U8TEXT; 236 | int hCrtStdOut = _open_osfhandle((intptr_t) GetStdHandle(STD_OUTPUT_HANDLE), flags); 237 | int hCrtStdErr = _open_osfhandle((intptr_t) GetStdHandle(STD_ERROR_HANDLE), flags); 238 | FILE *hfStdOut = (hCrtStdOut >= 0) ? _fdopen(hCrtStdOut, "wb") : NULL; 239 | FILE *hfStdErr = (hCrtStdErr >= 0) ? _fdopen(hCrtStdErr, "wb") : NULL; 240 | if(hfStdOut) { *stdout = *hfStdOut; /*std::cout.rdbuf(new std::filebuf(hfStdOut));*/ } 241 | if(hfStdErr) { *stderr = *hfStdErr; /*std::cerr.rdbuf(new std::filebuf(hfStdErr));*/ } 242 | 243 | HWND hwndConsole = GetConsoleWindow(); 244 | if((hwndConsole != NULL) && (hwndConsole != INVALID_HANDLE_VALUE)) 245 | { 246 | HMENU hMenu = GetSystemMenu(hwndConsole, 0); 247 | EnableMenuItem(hMenu, SC_CLOSE, MF_BYCOMMAND | MF_GRAYED); 248 | RemoveMenu(hMenu, SC_CLOSE, MF_BYCOMMAND); 249 | 250 | SetWindowPos(hwndConsole, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED); 251 | SetWindowLong(hwndConsole, GWL_STYLE, GetWindowLong(hwndConsole, GWL_STYLE) & (~WS_MAXIMIZEBOX) & (~WS_MINIMIZEBOX)); 252 | SetWindowPos(hwndConsole, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED); 253 | } 254 | 255 | if(HMODULE kernel32 = GetModuleHandleA("kernel32.dll")) 256 | { 257 | if(PSetConsoleIcon setConsoleIcon = (PSetConsoleIcon) GetProcAddress(kernel32, "SetConsoleIcon")) 258 | { 259 | if(HICON hIcon = LoadIcon(GetModuleHandle(0), MAKEINTRESOURCE(IDI_ICON1))) 260 | { 261 | setConsoleIcon(hIcon); 262 | } 263 | } 264 | } 265 | } 266 | } 267 | 268 | void printConsole(const char* text, const int &logLevel) 269 | { 270 | static const WORD COLORS[3] = 271 | { 272 | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY, 273 | FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY, 274 | FOREGROUND_RED | FOREGROUND_INTENSITY, 275 | }; 276 | 277 | CSLocker csLocker(g_consoleLock); 278 | 279 | if((g_hConsole == NULL) || (g_hConsole == INVALID_HANDLE_VALUE)) 280 | { 281 | return; /*console not ready*/ 282 | } 283 | 284 | if((logLevel >= 0) && (logLevel <= 2)) 285 | { 286 | SetConsoleTextAttribute(g_hConsole, COLORS[logLevel]); 287 | } 288 | 289 | char buffer[256]; 290 | strncpy_s(buffer, 256, text, _TRUNCATE); 291 | const size_t len = lstrlenA(buffer); 292 | buffer[len] = '\n'; //replace \0 with \n 293 | 294 | DWORD written; 295 | WriteConsoleA(g_hConsole, buffer, len+1, &written, NULL); 296 | } 297 | 298 | //=================================================================== 299 | // Utility Functions 300 | //=================================================================== 301 | 302 | 303 | quint32 getCurrentThread(void) 304 | { 305 | return GetCurrentThreadId(); 306 | } 307 | 308 | void shellExplore(const wchar_t *path) 309 | { 310 | wchar_t commandLine[512]; 311 | _snwprintf_s(commandLine, 512, _TRUNCATE, L"explorer.exe /select,\"%s\"", path); 312 | 313 | STARTUPINFOW startupInfo; 314 | memset(&startupInfo, 0, sizeof(STARTUPINFOW)); 315 | startupInfo.cb = sizeof(STARTUPINFOW); 316 | 317 | PROCESS_INFORMATION processInfo; 318 | memset(&processInfo, 0, sizeof(PROCESS_INFORMATION)); 319 | 320 | if(CreateProcessW(NULL, commandLine, NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo)) 321 | { 322 | CloseHandle(processInfo.hProcess); 323 | CloseHandle(processInfo.hThread); 324 | } 325 | } 326 | 327 | static HICON qicon2hicon(const QIcon &icon, const int w, const int h) 328 | { 329 | if(!icon.isNull()) 330 | { 331 | QPixmap pixmap = icon.pixmap(w, h); 332 | if(!pixmap.isNull()) 333 | { 334 | return pixmap.toWinHICON(); 335 | } 336 | } 337 | return NULL; 338 | } 339 | 340 | void changeWindowIcon(QWidget *window, const QIcon &icon, const bool bIsBigIcon) 341 | { 342 | if(!icon.isNull()) 343 | { 344 | const int extend = (bIsBigIcon ? 32 : 16); 345 | if(HICON hIcon = qicon2hicon(icon, extend, extend)) 346 | { 347 | SendMessage(window->winId(), WM_SETICON, (bIsBigIcon ? ICON_BIG : ICON_SMALL), LPARAM(hIcon)); 348 | } 349 | } 350 | } 351 | 352 | QString getEnvString(const QString &name) 353 | { 354 | wchar_t buffer[512]; 355 | DWORD ret = GetEnvironmentVariableW((const wchar_t*)name.utf16(), buffer, 512); 356 | if((ret > 0) && (ret < 512)) 357 | { 358 | return QString::fromUtf16((const ushort*)buffer); 359 | } 360 | return QString(); 361 | } 362 | -------------------------------------------------------------------------------- /src/System.h: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Double File Scanner 3 | // Copyright (C) 2014-2017 LoRd_MuldeR 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version, but always including the *additional* 9 | // restrictions defined in the "License.txt" file. 10 | // 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License along 17 | // with this program; if not, write to the Free Software Foundation, Inc., 18 | // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | // 20 | // http://www.gnu.org/licenses/gpl-2.0.txt 21 | /////////////////////////////////////////////////////////////////////////////// 22 | 23 | #pragma once 24 | 25 | #include 26 | 27 | class QWidget; 28 | class QIcon; 29 | 30 | void crashHandler(const char *message); 31 | void initConsole(void); 32 | void initErrorHandlers(void); 33 | void printConsole(const char* text, const int &logLevel); 34 | quint32 getCurrentThread(void); 35 | void changeWindowIcon(QWidget *window, const QIcon &icon, const bool bIsBigIcon = true); 36 | void shellExplore(const wchar_t *path); 37 | QString getEnvString(const QString &name); 38 | -------------------------------------------------------------------------------- /src/Taskbar.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Double File Scanner 3 | // Copyright (C) 2014-2017 LoRd_MuldeR 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version, but always including the *additional* 9 | // restrictions defined in the "License.txt" file. 10 | // 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License along 17 | // with this program; if not, write to the Free Software Foundation, Inc., 18 | // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | // 20 | // http://www.gnu.org/licenses/gpl-2.0.txt 21 | /////////////////////////////////////////////////////////////////////////////// 22 | 23 | #include "Taskbar.h" 24 | #include "Config.h" 25 | 26 | #include 27 | #include 28 | #include 29 | 30 | //Windows includes 31 | #define NOMINMAX 32 | #define WIN32_LEAN_AND_MEAN 33 | #include 34 | #include 35 | 36 | #define RELEASE(X) do \ 37 | { \ 38 | (X)->Release(); \ 39 | (X) = NULL; \ 40 | } \ 41 | while(0) 42 | 43 | //=================================================================== 44 | // Taskbar Data 45 | //=================================================================== 46 | 47 | class TaskbarData 48 | { 49 | friend class Taskbar; 50 | 51 | protected: 52 | TaskbarData(void) 53 | { 54 | winMsg = 0; 55 | ptbl = NULL; 56 | } 57 | 58 | UINT winMsg; 59 | ITaskbarList3 *ptbl; 60 | }; 61 | 62 | //=================================================================== 63 | // Constructor & Destructor 64 | //=================================================================== 65 | 66 | QMutex Taskbar::s_lock; 67 | TaskbarData *Taskbar::s_data = NULL; 68 | 69 | Taskbar::Taskbar(void) 70 | { 71 | throw std::runtime_error("Cannot create instance of this class!"); 72 | } 73 | 74 | Taskbar::~Taskbar(void) 75 | { 76 | } 77 | 78 | //=================================================================== 79 | // Public Functions 80 | //=================================================================== 81 | 82 | void Taskbar::init(void) 83 | { 84 | QMutexLocker lock(&s_lock); 85 | 86 | if(!s_data) 87 | { 88 | s_data = new TaskbarData(); 89 | s_data->winMsg = RegisterWindowMessageW(L"TaskbarButtonCreated"); 90 | 91 | if(s_data->winMsg == 0) 92 | { 93 | qWarning("RegisterWindowMessage() has failed!"); 94 | } 95 | } 96 | } 97 | 98 | void Taskbar::uninit(void) 99 | { 100 | QMutexLocker lock(&s_lock); 101 | 102 | if(s_data) 103 | { 104 | if(s_data->ptbl) 105 | { 106 | RELEASE(s_data->ptbl); 107 | CoUninitialize(); 108 | } 109 | MY_DELETE(s_data); 110 | } 111 | } 112 | 113 | bool Taskbar::handleWinEvent(void *message, long *result) 114 | { 115 | QMutexLocker lock(&s_lock); 116 | 117 | if(s_data && (s_data->winMsg != 0)) 118 | { 119 | if(((MSG*)message)->message == s_data->winMsg) 120 | { 121 | if(!s_data->ptbl) 122 | { 123 | *result = createInterface() ? S_OK : S_FALSE; 124 | } 125 | return true; 126 | } 127 | } 128 | 129 | return false; 130 | } 131 | 132 | bool Taskbar::setTaskbarState(QWidget *window, TaskbarState state) 133 | { 134 | QMutexLocker lock(&s_lock); 135 | 136 | if(s_data && s_data->ptbl && window) 137 | { 138 | HRESULT hr = HRESULT(-1); 139 | 140 | switch(state) 141 | { 142 | case TaskbarNoState: 143 | hr = s_data->ptbl->SetProgressState(reinterpret_cast(window->winId()), TBPF_NOPROGRESS); 144 | break; 145 | case TaskbarNormalState: 146 | hr = s_data->ptbl->SetProgressState(reinterpret_cast(window->winId()), TBPF_NORMAL); 147 | break; 148 | case TaskbarIndeterminateState: 149 | hr = s_data->ptbl->SetProgressState(reinterpret_cast(window->winId()), TBPF_INDETERMINATE); 150 | break; 151 | case TaskbarErrorState: 152 | hr = s_data->ptbl->SetProgressState(reinterpret_cast(window->winId()), TBPF_ERROR); 153 | break; 154 | case TaskbarPausedState: 155 | hr = s_data->ptbl->SetProgressState(reinterpret_cast(window->winId()), TBPF_PAUSED); 156 | break; 157 | } 158 | 159 | return SUCCEEDED(hr); 160 | } 161 | 162 | return false; 163 | } 164 | 165 | void Taskbar::setTaskbarProgress(QWidget *window, unsigned __int64 currentValue, unsigned __int64 maximumValue) 166 | { 167 | QMutexLocker lock(&s_lock); 168 | 169 | if(s_data && s_data->ptbl && window) 170 | { 171 | s_data->ptbl->SetProgressValue(reinterpret_cast(window->winId()), currentValue, maximumValue); 172 | } 173 | } 174 | 175 | void Taskbar::setOverlayIcon(QWidget *window, QIcon *icon) 176 | { 177 | QMutexLocker lock(&s_lock); 178 | 179 | if(s_data && s_data->ptbl && window) 180 | { 181 | s_data->ptbl->SetOverlayIcon(window->winId(), (icon ? icon->pixmap(16,16).toWinHICON() : NULL), L"DoubeFileScanner"); 182 | } 183 | } 184 | 185 | //=================================================================== 186 | // Private Functions 187 | //=================================================================== 188 | 189 | bool Taskbar::createInterface(void) 190 | { 191 | if(s_data && (!s_data->ptbl)) 192 | { 193 | const HRESULT hrComInit = CoInitialize(NULL); 194 | 195 | if((hrComInit != S_OK) && (hrComInit != S_FALSE)) 196 | { 197 | qWarning("CoInitialize() has failed!"); 198 | return false; 199 | } 200 | 201 | ITaskbarList3 *tmp = NULL; 202 | const HRESULT hrCreate = CoCreateInstance(CLSID_TaskbarList, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&tmp)); 203 | 204 | if(!SUCCEEDED(hrCreate)) 205 | { 206 | qWarning("ITaskbarList3 interface could not be created!"); 207 | CoUninitialize(); 208 | return false; 209 | } 210 | 211 | const HRESULT hrInitTaskbar = tmp->HrInit(); 212 | 213 | if(!SUCCEEDED(hrInitTaskbar)) 214 | { 215 | qWarning("ITaskbarList3::HrInit() has failed!"); 216 | RELEASE(tmp); 217 | CoUninitialize(); 218 | return false; 219 | } 220 | 221 | s_data->ptbl = tmp; 222 | return true; 223 | } 224 | 225 | qWarning("Interface was already created!"); 226 | return false; 227 | } 228 | -------------------------------------------------------------------------------- /src/Taskbar.h: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Double File Scanner 3 | // Copyright (C) 2014-2017 LoRd_MuldeR 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version, but always including the *additional* 9 | // restrictions defined in the "License.txt" file. 10 | // 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License along 17 | // with this program; if not, write to the Free Software Foundation, Inc., 18 | // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | // 20 | // http://www.gnu.org/licenses/gpl-2.0.txt 21 | /////////////////////////////////////////////////////////////////////////////// 22 | 23 | #pragma once 24 | 25 | #include 26 | 27 | class QWidget; 28 | class QIcon; 29 | class TaskbarData; 30 | 31 | class Taskbar 32 | { 33 | public: 34 | Taskbar(void); 35 | ~Taskbar(void); 36 | 37 | //Taskbar states 38 | enum TaskbarState 39 | { 40 | TaskbarNoState = 0, 41 | TaskbarNormalState = 1, 42 | TaskbarIndeterminateState = 2, 43 | TaskbarPausedState = 3, 44 | TaskbarErrorState = 4 45 | }; 46 | 47 | //Public interface 48 | static bool handleWinEvent(void *message, long *result); 49 | static bool setTaskbarState(QWidget *window, TaskbarState state); 50 | static void setTaskbarProgress(QWidget *window, unsigned __int64 currentValue, unsigned __int64 maximumValue); 51 | static void setOverlayIcon(QWidget *window, QIcon *icon); 52 | 53 | static void init(void); 54 | static void uninit(void); 55 | 56 | private: 57 | static bool createInterface(void); 58 | static QMutex s_lock; 59 | static TaskbarData *s_data; 60 | }; 61 | -------------------------------------------------------------------------------- /src/Thread_DirectoryScanner.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Double File Scanner 3 | // Copyright (C) 2014-2017 LoRd_MuldeR 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version, but always including the *additional* 9 | // restrictions defined in the "License.txt" file. 10 | // 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License along 17 | // with this program; if not, write to the Free Software Foundation, Inc., 18 | // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | // 20 | // http://www.gnu.org/licenses/gpl-2.0.txt 21 | /////////////////////////////////////////////////////////////////////////////// 22 | 23 | #include "Thread_DirectoryScanner.h" 24 | 25 | #include "Config.h" 26 | #include "System.h" 27 | 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include 35 | 36 | static const quint64 MAX_ENQUEUED_TASKS = 128; 37 | static const QStringList EMPTY_STRINGLIST; 38 | 39 | //======================================================================================= 40 | // Directory Scanner 41 | //======================================================================================= 42 | 43 | DirectoryScanner::DirectoryScanner(volatile bool *abortFlag, const int &threadCount, const bool recursive) 44 | : 45 | m_abortFlag(abortFlag), 46 | m_recusrive(recursive) 47 | { 48 | this->moveToThread(this); 49 | 50 | m_pendingTasks = 0; 51 | m_pool = new QThreadPool(); 52 | m_pauseFlag = false; 53 | 54 | if(threadCount > 0) 55 | { 56 | m_pool->setMaxThreadCount(qBound(1, threadCount, 64)); 57 | } 58 | } 59 | 60 | DirectoryScanner::~DirectoryScanner(void) 61 | { 62 | //qDebug("DirectoryScanner deleted."); 63 | MY_DELETE(m_pool); 64 | } 65 | 66 | void DirectoryScanner::run(void) 67 | { 68 | qDebug("[Scanning Directory]"); 69 | //qWarning("DirectoryScanner::run: Current thread id = %u", getCurrentThread()); 70 | 71 | m_files.clear(); 72 | m_pendingTasks = 0; 73 | 74 | if(m_pendingDirs.count() < 1) 75 | { 76 | qWarning("File list is empty -> Nothing to do!"); 77 | return; 78 | } 79 | 80 | qDebug("Pending dirs: %d", m_pendingDirs.count()); 81 | 82 | while((!m_pendingDirs.empty()) && (m_pendingTasks < MAX_ENQUEUED_TASKS)) 83 | { 84 | scanDirectory(m_pendingDirs.dequeue()); 85 | } 86 | 87 | exec(); 88 | 89 | if(!m_pendingDirs.empty()) 90 | { 91 | qWarning("Thread is about to exit while there still are pending directories!"); 92 | m_pendingDirs.clear(); 93 | } 94 | 95 | while(!m_pool->waitForDone(5000)) 96 | { 97 | qWarning("Still have running taks -> waiting for completeion!"); 98 | } 99 | 100 | qDebug("Found %d files!", m_files.count()); 101 | qDebug("Thread will exit!\n"); 102 | } 103 | 104 | void DirectoryScanner::scanDirectory(const QString path) 105 | { 106 | sleepWhilePaused(); 107 | 108 | DirectoryScannerTask *task = new DirectoryScannerTask(path, m_abortFlag); 109 | if(connect(task, SIGNAL(directoryAnalyzed(const QStringList*, const QStringList*)), this, SLOT(directoryDone(const QStringList*, const QStringList*)), Qt::BlockingQueuedConnection)) 110 | { 111 | m_pendingTasks++; 112 | m_pool->start(task); 113 | } 114 | } 115 | 116 | void DirectoryScanner::directoryDone(const QStringList *files, const QStringList *dirs) 117 | { 118 | for(QStringList::ConstIterator iter = files->constBegin(); iter != files->constEnd(); iter++) 119 | { 120 | m_files.insert(*iter); 121 | } 122 | 123 | if(m_recusrive) 124 | { 125 | m_pendingDirs << (*dirs); 126 | } 127 | 128 | while((!m_pendingDirs.empty()) && (m_pendingTasks < MAX_ENQUEUED_TASKS) && (!(*m_abortFlag))) 129 | { 130 | scanDirectory(m_pendingDirs.dequeue()); 131 | } 132 | 133 | assert(m_pendingTasks > 0); 134 | 135 | if(--m_pendingTasks == 0) 136 | { 137 | qDebug("All tasks done!"); 138 | QTimer::singleShot(0, this, SLOT(quit())); 139 | } 140 | } 141 | 142 | void DirectoryScanner::addDirectory(const QString &path) 143 | { 144 | if(this->isRunning()) 145 | { 146 | qWarning("Cannot add input while thread is still running!"); 147 | return; 148 | } 149 | 150 | m_pendingDirs << path; 151 | } 152 | 153 | void DirectoryScanner::addDirectories(const QStringList &paths) 154 | { 155 | if(this->isRunning()) 156 | { 157 | qWarning("Cannot add input while thread is still running!"); 158 | return; 159 | } 160 | 161 | m_pendingDirs << paths; 162 | } 163 | 164 | void DirectoryScanner::setRecursive(const bool &recusrive) 165 | { 166 | if(this->isRunning()) 167 | { 168 | qWarning("Cannot add input while thread is still running!"); 169 | return; 170 | } 171 | 172 | m_recusrive = recusrive; 173 | } 174 | 175 | const QStringList DirectoryScanner::getFiles(void) const 176 | { 177 | if(this->isRunning()) 178 | { 179 | qWarning("Result requested while thread is still running!"); 180 | return EMPTY_STRINGLIST; 181 | } 182 | 183 | QStringList fileList = m_files.values(); 184 | 185 | fileList.sort(); 186 | return fileList; 187 | } 188 | 189 | void DirectoryScanner::suspend(const bool bSuspend) 190 | { 191 | m_pauseLock.lock(); 192 | if(m_pauseFlag != bSuspend) 193 | { 194 | m_pauseFlag = bSuspend; 195 | m_pauseWait.wakeAll(); 196 | } 197 | m_pauseLock.unlock(); 198 | } 199 | 200 | void DirectoryScanner::sleepWhilePaused(void) 201 | { 202 | m_pauseLock.lock(); 203 | while(m_pauseFlag) 204 | { 205 | m_pauseWait.wait(&m_pauseLock); 206 | } 207 | m_pauseLock.unlock(); 208 | } 209 | 210 | //======================================================================================= 211 | // Directory Scanner Task 212 | //======================================================================================= 213 | 214 | DirectoryScannerTask::DirectoryScannerTask(const QString &directory, volatile bool *abortFlag) 215 | : 216 | m_directory(directory), 217 | m_abortFlag(abortFlag) 218 | { 219 | } 220 | 221 | DirectoryScannerTask::~DirectoryScannerTask(void) 222 | { 223 | //qDebug("DirectoryScannerTask deleted."); 224 | } 225 | 226 | void DirectoryScannerTask::run(void) 227 | { 228 | qDebug("%s", m_directory.toUtf8().constData()); 229 | 230 | QStringList files, dirs; 231 | QFileInfo dirInfo(m_directory); 232 | 233 | if((*m_abortFlag) || (!(dirInfo.exists() && dirInfo.isDir()))) 234 | { 235 | emit directoryAnalyzed(&files, &dirs); 236 | return; 237 | } 238 | 239 | QDirIterator iter(m_directory, QDir::AllEntries | QDir::NoDotAndDotDot | QDir::Hidden, QDirIterator::NoIteratorFlags); 240 | 241 | while(iter.hasNext() && (!(*m_abortFlag))) 242 | { 243 | const QString path = iter.next(); 244 | const QFileInfo info = iter.fileInfo(); 245 | 246 | if(info.exists()) 247 | { 248 | if(info.isFile()) 249 | { 250 | files << info.canonicalFilePath(); 251 | } 252 | else if(info.isDir()) 253 | { 254 | dirs << info.canonicalFilePath(); 255 | } 256 | } 257 | } 258 | 259 | files.sort(); 260 | dirs.sort(); 261 | 262 | emit directoryAnalyzed(&files, &dirs); 263 | } 264 | -------------------------------------------------------------------------------- /src/Thread_DirectoryScanner.h: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Double File Scanner 3 | // Copyright (C) 2014-2017 LoRd_MuldeR 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version, but always including the *additional* 9 | // restrictions defined in the "License.txt" file. 10 | // 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License along 17 | // with this program; if not, write to the Free Software Foundation, Inc., 18 | // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | // 20 | // http://www.gnu.org/licenses/gpl-2.0.txt 21 | /////////////////////////////////////////////////////////////////////////////// 22 | 23 | #pragma once 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | class QThreadPool; 34 | class QEventLoop; 35 | 36 | //======================================================================================= 37 | 38 | class DirectoryScannerTask : public QObject, public QRunnable 39 | { 40 | Q_OBJECT 41 | 42 | public: 43 | DirectoryScannerTask(const QString &directory, volatile bool *abortFlag); 44 | virtual ~DirectoryScannerTask(void); 45 | 46 | signals: 47 | void directoryAnalyzed(const QStringList *files, const QStringList *dirs); 48 | 49 | protected: 50 | virtual void run(void); 51 | 52 | const QString m_directory; 53 | volatile bool *const m_abortFlag; 54 | }; 55 | 56 | //======================================================================================= 57 | 58 | class DirectoryScanner : public QThread 59 | { 60 | Q_OBJECT 61 | 62 | public: 63 | DirectoryScanner(volatile bool *abortFlag, const int &threadCount = -1, const bool recursive = true); 64 | virtual ~DirectoryScanner(void); 65 | 66 | void setRecursive(const bool &recusrive); 67 | void addDirectory(const QString &path); 68 | void addDirectories(const QStringList &paths); 69 | void suspend(const bool bSuspend); 70 | 71 | const QStringList getFiles(void) const; 72 | 73 | private slots: 74 | void directoryDone(const QStringList *files, const QStringList *dirs); 75 | 76 | protected: 77 | virtual void run(void); 78 | void scanDirectory(const QString path); 79 | void sleepWhilePaused(void); 80 | 81 | bool m_recusrive; 82 | bool m_pauseFlag; 83 | 84 | QThreadPool* m_pool; 85 | QMutex m_pauseLock; 86 | QWaitCondition m_pauseWait; 87 | 88 | QQueue m_pendingDirs; 89 | QSet m_files; 90 | quint64 m_pendingTasks; 91 | 92 | volatile bool *const m_abortFlag; 93 | }; 94 | -------------------------------------------------------------------------------- /src/Thread_FileComparator.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Double File Scanner 3 | // Copyright (C) 2014-2017 LoRd_MuldeR 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version, but always including the *additional* 9 | // restrictions defined in the "License.txt" file. 10 | // 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License along 17 | // with this program; if not, write to the Free Software Foundation, Inc., 18 | // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | // 20 | // http://www.gnu.org/licenses/gpl-2.0.txt 21 | /////////////////////////////////////////////////////////////////////////////// 22 | 23 | #include "Thread_FileComparator.h" 24 | 25 | #include "Model_Duplicates.h" 26 | #include "Config.h" 27 | #include "System.h" 28 | 29 | #include "strnatcmp/strnatcmp.h" 30 | 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | 38 | #include 39 | 40 | static const quint64 MAX_ENQUEUED_TASKS = 128; 41 | static const QHash EMPTY_DUPLICATES_LIST; 42 | 43 | static bool filePathLessThan(const QString &s1, const QString &s2) 44 | { 45 | int result = 0; 46 | 47 | const wchar_t *file1 = wcsrchr((const wchar_t*) s1.utf16(), L'/'); 48 | const wchar_t *file2 = wcsrchr((const wchar_t*) s2.utf16(), L'/'); 49 | 50 | if(file1 && file2) 51 | { 52 | result = strnatcasecmp(++file1, ++file2); 53 | } 54 | 55 | if(result == 0) 56 | { 57 | result = strnatcasecmp((const wchar_t*) s1.utf16(), (const wchar_t*) s2.utf16()); 58 | } 59 | 60 | return (result < 0); 61 | } 62 | 63 | //======================================================================================= 64 | // File Comparator 65 | //======================================================================================= 66 | 67 | FileComparator::FileComparator(volatile bool *abortFlag, const int &threadCount) 68 | : 69 | m_abortFlag(abortFlag) 70 | { 71 | this->moveToThread(this); 72 | 73 | m_pendingTasks = 0; 74 | m_pool = new QThreadPool(); 75 | m_pauseFlag = false; 76 | 77 | m_completedFileCount = 0; 78 | m_totalFileCount = m_files.count(); 79 | m_progressValue = -1; 80 | 81 | if(threadCount > 0) 82 | { 83 | m_pool->setMaxThreadCount(qBound(1, threadCount, 64)); 84 | } 85 | } 86 | 87 | FileComparator::~FileComparator(void) 88 | { 89 | //qDebug("FileComparator deleted."); 90 | MY_DELETE(m_pool); 91 | } 92 | 93 | void FileComparator::run(void) 94 | { 95 | qDebug("[Analyzing Files]"); 96 | //qWarning("FileComparator::run: Current thread id = %u", getCurrentThread()); 97 | 98 | m_hashes.clear(); 99 | m_fileSizes.clear(); 100 | 101 | m_pendingTasks = 0; 102 | 103 | m_completedFileCount = 0; 104 | m_totalFileCount = m_files.count(); 105 | m_progressValue = -1; 106 | 107 | if(m_files.count() < 1) 108 | { 109 | qWarning("File list is empty -> Nothing to do!"); 110 | emit progressChanged(100); 111 | return; 112 | } 113 | 114 | while((!m_files.empty()) && (m_pendingTasks < MAX_ENQUEUED_TASKS)) 115 | { 116 | scanNextFile(m_files.dequeue()); 117 | } 118 | 119 | exec(); 120 | 121 | if(!m_files.empty()) 122 | { 123 | qWarning("Thread is about to exit while there still are pending directories!"); 124 | m_files.clear(); 125 | } 126 | 127 | while(!m_pool->waitForDone(5000)) 128 | { 129 | qWarning("Still have running taks -> waiting for completeion!"); 130 | //QEventLoop loop; loop.processEvents(); 131 | } 132 | 133 | if(!(*m_abortFlag)) 134 | { 135 | qDebug("\n[Searching Duplicates]"); 136 | 137 | quint32 duplicateCount = 0; 138 | QList keys = m_hashes.uniqueKeys(); 139 | qSort(keys); 140 | 141 | for(QList::ConstIterator iter = keys.constBegin(); iter != keys.constEnd(); iter++) 142 | { 143 | const int count = m_hashes.count(*iter); 144 | if(count > 1) 145 | { 146 | qDebug("%s -> %d", iter->toHex().constData(), count); 147 | QList values = m_hashes.values(*iter); 148 | qSort(values.begin(), values.end(), filePathLessThan); 149 | emit duplicateFound((*iter), values, m_fileSizes.value((*iter), 0)); 150 | duplicateCount++; 151 | } 152 | } 153 | 154 | qDebug("Found %d files with duplicates!", duplicateCount); 155 | emit progressChanged(100); 156 | } 157 | 158 | m_hashes.clear(); 159 | m_fileSizes.clear(); 160 | 161 | qDebug("Thread will exit!\n"); 162 | } 163 | 164 | void FileComparator::scanNextFile(const QString path) 165 | { 166 | sleepWhilePaused(); 167 | 168 | FileComparatorTask *task = new FileComparatorTask(path, m_abortFlag); 169 | if(connect(task, SIGNAL(fileAnalyzed(const QByteArray&, const QString&, const qint64&)), this, SLOT(fileDone(const QByteArray&, const QString&, const qint64&)), Qt::BlockingQueuedConnection)) 170 | { 171 | m_pendingTasks++; 172 | m_pool->start(task); 173 | } 174 | } 175 | 176 | void FileComparator::fileDone(const QByteArray &hash, const QString &path, const qint64 &fileSize) 177 | { 178 | if(!(hash.isEmpty() || path.isEmpty() || (fileSize < 0))) 179 | { 180 | m_hashes.insertMulti(hash, path); 181 | 182 | if(!m_fileSizes.contains(hash)) 183 | { 184 | m_fileSizes.insert(hash, fileSize); /*store new file size*/ 185 | } 186 | else if(m_fileSizes.value(hash) != fileSize) 187 | { 188 | qFatal("Madness: SHA-1 collission has been detected!"); 189 | } 190 | } 191 | 192 | const int progress = qRound(double(++m_completedFileCount) / double(m_totalFileCount) * 99.0); 193 | 194 | if((progress > m_progressValue) && (!(*m_abortFlag))) 195 | { 196 | m_progressValue = progress; 197 | emit progressChanged(m_progressValue); 198 | } 199 | 200 | while((!m_files.empty()) && (m_pendingTasks < MAX_ENQUEUED_TASKS) && (!(*m_abortFlag))) 201 | { 202 | scanNextFile(m_files.dequeue()); 203 | } 204 | 205 | assert(m_pendingTasks > 0); 206 | 207 | if(--m_pendingTasks == 0) 208 | { 209 | qDebug("All tasks done!"); 210 | QTimer::singleShot(0, this, SLOT(quit())); 211 | } 212 | } 213 | 214 | void FileComparator::addFiles(const QStringList &files) 215 | { 216 | if(this->isRunning()) 217 | { 218 | qWarning("Cannot add input while thread is still running!"); 219 | return; 220 | } 221 | 222 | m_files << files; 223 | } 224 | 225 | void FileComparator::suspend(const bool bSuspend) 226 | { 227 | m_pauseLock.lock(); 228 | if(m_pauseFlag != bSuspend) 229 | { 230 | m_pauseFlag = bSuspend; 231 | m_pauseWait.wakeAll(); 232 | } 233 | m_pauseLock.unlock(); 234 | } 235 | 236 | void FileComparator::sleepWhilePaused(void) 237 | { 238 | m_pauseLock.lock(); 239 | while(m_pauseFlag) 240 | { 241 | m_pauseWait.wait(&m_pauseLock); 242 | } 243 | m_pauseLock.unlock(); 244 | } 245 | 246 | //======================================================================================= 247 | // File Comparator Task 248 | //======================================================================================= 249 | 250 | FileComparatorTask::FileComparatorTask(const QString &filePath, volatile bool *abortFlag) 251 | : 252 | m_filePath(filePath), 253 | m_abortFlag(abortFlag) 254 | { 255 | } 256 | 257 | FileComparatorTask::~FileComparatorTask(void) 258 | { 259 | //qDebug("FileComparatorTask deleted."); 260 | } 261 | 262 | void FileComparatorTask::run(void) 263 | { 264 | if(*m_abortFlag) 265 | { 266 | emit fileAnalyzed(QByteArray(), QString(), -1); 267 | return; 268 | } 269 | 270 | qDebug("%s", m_filePath.toUtf8().constData()); 271 | 272 | QFile file(m_filePath); 273 | 274 | if(file.open(QIODevice::ReadOnly)) 275 | { 276 | qint64 fileSize = 0; 277 | QCryptographicHash hash(QCryptographicHash::Sha1); 278 | 279 | while(!(file.atEnd() || (file.error() != QFile::NoError) || (*m_abortFlag))) 280 | { 281 | const QByteArray buffer = file.read(4096 /*1048576*/); 282 | if(buffer.size() > 0) 283 | { 284 | hash.addData(buffer); 285 | fileSize += buffer.size(); 286 | } 287 | } 288 | 289 | const QFile::FileError error = file.error(); 290 | file.close(); 291 | 292 | if((error == QFile::NoError) && (!(*m_abortFlag))) 293 | { 294 | emit fileAnalyzed(hash.result(), m_filePath, fileSize); 295 | return; 296 | } 297 | } 298 | 299 | if(!(*m_abortFlag)) 300 | { 301 | qWarning("Failed to open: %s", m_filePath.toUtf8().constData()); 302 | } 303 | 304 | emit fileAnalyzed(QByteArray(), QString(), -1); 305 | } 306 | -------------------------------------------------------------------------------- /src/Thread_FileComparator.h: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Double File Scanner 3 | // Copyright (C) 2014-2017 LoRd_MuldeR 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version, but always including the *additional* 9 | // restrictions defined in the "License.txt" file. 10 | // 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License along 17 | // with this program; if not, write to the Free Software Foundation, Inc., 18 | // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | // 20 | // http://www.gnu.org/licenses/gpl-2.0.txt 21 | /////////////////////////////////////////////////////////////////////////////// 22 | 23 | #pragma once 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | class QThreadPool; 35 | class QEventLoop; 36 | class DuplicatesModel; 37 | 38 | //======================================================================================= 39 | 40 | class FileComparatorTask : public QObject, public QRunnable 41 | { 42 | Q_OBJECT 43 | 44 | public: 45 | FileComparatorTask(const QString &filePath, volatile bool *abortFlag); 46 | virtual ~FileComparatorTask(void); 47 | 48 | signals: 49 | void fileAnalyzed(const QByteArray &hash, const QString &path, const qint64 &fileSize); 50 | 51 | protected: 52 | virtual void run(void); 53 | 54 | const QString m_filePath; 55 | volatile bool* const m_abortFlag; 56 | }; 57 | 58 | //======================================================================================= 59 | 60 | class FileComparator : public QThread 61 | { 62 | Q_OBJECT 63 | 64 | public: 65 | FileComparator(volatile bool *abortFlag, const int &threadCount = -1); 66 | virtual ~FileComparator(void); 67 | 68 | void addFiles(const QStringList &files); 69 | void suspend(const bool bSuspend); 70 | 71 | private slots: 72 | void fileDone(const QByteArray &hash, const QString &path, const qint64 &fileSize); 73 | 74 | signals: 75 | void progressChanged(const int &progress); 76 | void duplicateFound(const QByteArray &hash, const QStringList &path, const qint64 size); 77 | 78 | protected: 79 | virtual void run(void); 80 | void scanNextFile(const QString path); 81 | void sleepWhilePaused(void); 82 | 83 | bool m_pauseFlag; 84 | 85 | QThreadPool* m_pool; 86 | QMutex m_pauseLock; 87 | QWaitCondition m_pauseWait; 88 | 89 | QQueue m_files; 90 | quint64 m_pendingTasks; 91 | 92 | QHash m_hashes; 93 | QHash m_fileSizes; 94 | 95 | int m_totalFileCount; 96 | int m_completedFileCount; 97 | int m_progressValue; 98 | 99 | volatile bool *const m_abortFlag; 100 | }; 101 | -------------------------------------------------------------------------------- /src/Utilities.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Double File Scanner 3 | // Copyright (C) 2014-2017 LoRd_MuldeR 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version, but always including the *additional* 9 | // restrictions defined in the "License.txt" file. 10 | // 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License along 17 | // with this program; if not, write to the Free Software Foundation, Inc., 18 | // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | // 20 | // http://www.gnu.org/licenses/gpl-2.0.txt 21 | /////////////////////////////////////////////////////////////////////////////// 22 | 23 | #pragma once 24 | 25 | #include "Utilities.h" 26 | 27 | QString Utilities::sizeToString(const qint64 &size) 28 | { 29 | static const struct 30 | { 31 | const char *suffix; 32 | const qint64 size; 33 | } 34 | SIZE[] = 35 | { 36 | { "KB", 1024i64 }, 37 | { "MB", 1048576i64 }, 38 | { "GB", 1073741824i64 }, 39 | { "TB", 1099511627776i64 }, 40 | { "PB", 1125899906842624i64 } 41 | }; 42 | 43 | 44 | int idx = 0; 45 | 46 | while((size >= SIZE[idx+1].size) && (idx < 3)) 47 | { 48 | idx++; 49 | } 50 | 51 | return QString().sprintf("%.2f %s", double(size)/double(SIZE[idx].size), SIZE[idx].suffix); 52 | } 53 | -------------------------------------------------------------------------------- /src/Utilities.h: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Double File Scanner 3 | // Copyright (C) 2014-2017 LoRd_MuldeR 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version, but always including the *additional* 9 | // restrictions defined in the "License.txt" file. 10 | // 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License along 17 | // with this program; if not, write to the Free Software Foundation, Inc., 18 | // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | // 20 | // http://www.gnu.org/licenses/gpl-2.0.txt 21 | /////////////////////////////////////////////////////////////////////////////// 22 | 23 | #pragma once 24 | 25 | #include 26 | 27 | class Utilities 28 | { 29 | public: 30 | static QString sizeToString(const qint64 &size); 31 | 32 | private: 33 | Utilities(void) {} 34 | ~Utilities(void) {} 35 | }; -------------------------------------------------------------------------------- /src/Version.h: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Double File Scanner 3 | // Copyright (C) 2014-2017 LoRd_MuldeR 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version, but always including the *additional* 9 | // restrictions defined in the "License.txt" file. 10 | // 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License along 17 | // with this program; if not, write to the Free Software Foundation, Inc., 18 | // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | // 20 | // http://www.gnu.org/licenses/gpl-2.0.txt 21 | /////////////////////////////////////////////////////////////////////////////// 22 | 23 | #ifdef DBLSCAN_VERSION 24 | #undef DBLSCAN_VERSION 25 | 26 | #define DBLSCAN_VER_MAJOR 2 27 | #define DBLSCAN_VER_MINOR_HI 0 28 | #define DBLSCAN_VER_MAJOR_LO 4 29 | #define DBLSCAN_VER_PATCH 0 30 | 31 | #define DBLSCAN_VER_STRING_HLP1(X) #X 32 | #define DBLSCAN_VER_STRING_HLP2(W,X,Y,Z) DBLSCAN_VER_STRING_HLP1(v##W.X##Y-Z) 33 | #define DBLSCAN_VER_STRING_HLP3(W,X,Y,Z) DBLSCAN_VER_STRING_HLP2(W,X,Y,Z) 34 | #define DBLSCAN_VER_STRING DBLSCAN_VER_STRING_HLP3(DBLSCAN_VER_MAJOR,DBLSCAN_VER_MINOR_HI,DBLSCAN_VER_MAJOR_LO,DBLSCAN_VER_PATCH) 35 | 36 | #endif //DBLSCAN_VERSION 37 | -------------------------------------------------------------------------------- /src/Window_Directories.cpp: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Double File Scanner 3 | // Copyright (C) 2014-2017 LoRd_MuldeR 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version, but always including the *additional* 9 | // restrictions defined in the "License.txt" file. 10 | // 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License along 17 | // with this program; if not, write to the Free Software Foundation, Inc., 18 | // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | // 20 | // http://www.gnu.org/licenses/gpl-2.0.txt 21 | /////////////////////////////////////////////////////////////////////////////// 22 | 23 | #include "Window_Directories.h" 24 | #include "UIC_Window_Directories.h" 25 | 26 | #include "Config.h" 27 | #include "System.h" 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include 38 | 39 | //=================================================================== 40 | // Constructor & Destructor 41 | //=================================================================== 42 | 43 | DirectoriesDialog::DirectoriesDialog(QWidget *const parent) 44 | : 45 | QDialog(parent), 46 | ui(new Ui::DirectoriesDialog()) 47 | { 48 | //Setup window flags 49 | setWindowFlags(windowFlags() | Qt::Tool); 50 | 51 | //Setup UI 52 | ui->setupUi(this); 53 | 54 | //Setup size 55 | setMinimumSize(size()); 56 | 57 | //Setup connections 58 | connect(ui->buttonAddDir, SIGNAL(clicked()), this, SLOT(addDirectory())); 59 | connect(ui->buttonClear, SIGNAL(clicked()), this, SLOT(clearDirectories())); 60 | connect(ui->buttonRemoveDir, SIGNAL(clicked()), this, SLOT(removeDirectory())); 61 | 62 | //Disable button initially 63 | ui->buttonOkay->setEnabled(false); 64 | 65 | //Set initial path 66 | m_lastPath = QDir::homePath(); 67 | 68 | //Create label 69 | m_label = new QLabel(ui->listWidget); 70 | m_label->setText(tr("Please add at least one directory...")); 71 | m_label->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter); 72 | 73 | 74 | //Configure font 75 | QFont font = m_label->font(); 76 | font.setBold(true); 77 | m_label->setFont(font); 78 | 79 | //Configure color 80 | QPalette palette = m_label->palette(); 81 | palette.setColor(QPalette::WindowText, QColor(Qt::darkGray)); 82 | m_label->setPalette(palette); 83 | 84 | //Enable drag&drop support 85 | setAcceptDrops(true); 86 | } 87 | 88 | DirectoriesDialog::~DirectoriesDialog(void) 89 | { 90 | MY_DELETE(m_label); 91 | delete ui; 92 | } 93 | 94 | //=================================================================== 95 | // Events 96 | //=================================================================== 97 | 98 | void DirectoriesDialog::showEvent(QShowEvent *e) 99 | { 100 | QDialog::showEvent(e); 101 | resizeEvent(NULL); 102 | 103 | if(ui->listWidget->count() < 1) 104 | { 105 | QTimer::singleShot(0, this, SLOT(addDirectory())); 106 | } 107 | } 108 | 109 | void DirectoriesDialog::resizeEvent(QResizeEvent *e) 110 | { 111 | QDialog::resizeEvent(e); 112 | m_label->resize(ui->listWidget->viewport()->size()); 113 | } 114 | 115 | void DirectoriesDialog::dragEnterEvent(QDragEnterEvent *e) 116 | { 117 | QStringList formats = e->mimeData()->formats(); 118 | 119 | if(formats.contains("application/x-qt-windows-mime;value=\"FileNameW\"", Qt::CaseInsensitive) && formats.contains("text/uri-list", Qt::CaseInsensitive)) 120 | { 121 | e->acceptProposedAction(); 122 | } 123 | } 124 | 125 | void DirectoriesDialog::dropEvent(QDropEvent *e) 126 | { 127 | QStringList droppedFolders; 128 | QList urls = e->mimeData()->urls(); 129 | 130 | for(QList::ConstIterator iter = urls.constBegin(); iter != urls.constEnd(); iter++) 131 | { 132 | QFileInfo item(iter->toLocalFile()); 133 | if(item.exists() && item.isDir()) 134 | { 135 | droppedFolders << item.canonicalFilePath(); 136 | } 137 | } 138 | 139 | if(!droppedFolders.isEmpty()) 140 | { 141 | addDirectories(droppedFolders); 142 | } 143 | } 144 | 145 | //=================================================================== 146 | // Slots 147 | //=================================================================== 148 | 149 | void DirectoriesDialog::addDirectory(void) 150 | { 151 | const QString path = QFileDialog::getExistingDirectory(this, tr("Choose Directory"), m_lastPath); 152 | 153 | if(!path.isEmpty()) 154 | { 155 | for(int i = 0; i < ui->listWidget->count(); i++) 156 | { 157 | if(QDir::fromNativeSeparators(path).compare(QDir::fromNativeSeparators(ui->listWidget->item(i)->text()), Qt::CaseInsensitive) == 0) 158 | { 159 | QMessageBox::warning(this, tr("Warning"), tr("The selected directory is already on the list!")); 160 | return; 161 | } 162 | } 163 | 164 | QListWidgetItem *item = new QListWidgetItem(QIcon(":/res/Icon_Folder.png"), QDir::toNativeSeparators(path), ui->listWidget); 165 | ui->listWidget->addItem(item); 166 | 167 | QDir lastPath(path); 168 | lastPath.cdUp(); 169 | m_lastPath = lastPath.path(); 170 | 171 | ui->buttonOkay->setEnabled(ui->listWidget->count() > 0); 172 | m_label->setVisible(ui->listWidget->count() < 1); 173 | } 174 | } 175 | 176 | void DirectoriesDialog::removeDirectory(void) 177 | { 178 | qDeleteAll(ui->listWidget->selectedItems()); 179 | ui->buttonOkay->setEnabled(ui->listWidget->count() > 0); 180 | m_label->setVisible(ui->listWidget->count() < 1); 181 | } 182 | 183 | void DirectoriesDialog::clearDirectories(void) 184 | { 185 | ui->listWidget->clear(); 186 | ui->buttonOkay->setEnabled(ui->listWidget->count() > 0); 187 | m_label->setVisible(ui->listWidget->count() < 1); 188 | } 189 | 190 | //=================================================================== 191 | // Public Functions 192 | //=================================================================== 193 | 194 | QStringList DirectoriesDialog::getDirectories(void) 195 | { 196 | QStringList directories; 197 | 198 | for(int i = 0; i < ui->listWidget->count(); i++) 199 | { 200 | directories << QDir::fromNativeSeparators(ui->listWidget->item(i)->text()); 201 | } 202 | 203 | return directories; 204 | } 205 | 206 | void DirectoriesDialog::addDirectories(const QStringList &directories) 207 | { 208 | for(QStringList::ConstIterator iter = directories.constBegin(); iter != directories.constEnd(); iter++) 209 | { 210 | bool bSkipFolder = false; 211 | 212 | for(int i = 0; i < ui->listWidget->count(); i++) 213 | { 214 | if(QDir::fromNativeSeparators(*iter).compare(QDir::fromNativeSeparators(ui->listWidget->item(i)->text()), Qt::CaseInsensitive) == 0) 215 | { 216 | bSkipFolder = true; 217 | break; 218 | } 219 | } 220 | 221 | if(!bSkipFolder) 222 | { 223 | QListWidgetItem *item = new QListWidgetItem(QIcon(":/res/Icon_Folder.png"), QDir::toNativeSeparators(*iter), ui->listWidget); 224 | ui->listWidget->addItem(item); 225 | } 226 | } 227 | 228 | ui->buttonOkay->setEnabled(ui->listWidget->count() > 0); 229 | m_label->setVisible(ui->listWidget->count() < 1); 230 | } 231 | 232 | bool DirectoriesDialog::getRecursive(void) 233 | { 234 | return ui->checkBoxRecursive->isChecked(); 235 | } 236 | -------------------------------------------------------------------------------- /src/Window_Directories.h: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Double File Scanner 3 | // Copyright (C) 2014-2017 LoRd_MuldeR 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version, but always including the *additional* 9 | // restrictions defined in the "License.txt" file. 10 | // 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License along 17 | // with this program; if not, write to the Free Software Foundation, Inc., 18 | // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | // 20 | // http://www.gnu.org/licenses/gpl-2.0.txt 21 | /////////////////////////////////////////////////////////////////////////////// 22 | 23 | #pragma once 24 | 25 | #include 26 | 27 | class QLabel; 28 | 29 | //UIC forward declartion 30 | namespace Ui { 31 | class DirectoriesDialog; 32 | } 33 | 34 | //DirectoriesDialog class 35 | class DirectoriesDialog: public QDialog 36 | { 37 | Q_OBJECT 38 | 39 | public: 40 | DirectoriesDialog(QWidget *const parent); 41 | virtual ~DirectoriesDialog(void); 42 | 43 | QStringList getDirectories(void); 44 | void addDirectories(const QStringList &directories); 45 | bool getRecursive(void); 46 | 47 | private slots: 48 | void addDirectory(void); 49 | void removeDirectory(void); 50 | void clearDirectories(void); 51 | 52 | protected: 53 | virtual void showEvent(QShowEvent *e); 54 | virtual void resizeEvent(QResizeEvent *e); 55 | virtual void dragEnterEvent(QDragEnterEvent *e); 56 | virtual void dropEvent(QDropEvent *e); 57 | 58 | QLabel *m_label; 59 | QString m_lastPath; 60 | 61 | Ui::DirectoriesDialog *const ui; 62 | }; 63 | -------------------------------------------------------------------------------- /src/Window_Main.h: -------------------------------------------------------------------------------- 1 | /////////////////////////////////////////////////////////////////////////////// 2 | // Double File Scanner 3 | // Copyright (C) 2014-2017 LoRd_MuldeR 4 | // 5 | // This program is free software; you can redistribute it and/or modify 6 | // it under the terms of the GNU General Public License as published by 7 | // the Free Software Foundation; either version 2 of the License, or 8 | // (at your option) any later version, but always including the *additional* 9 | // restrictions defined in the "License.txt" file. 10 | // 11 | // This program is distributed in the hope that it will be useful, 12 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | // GNU General Public License for more details. 15 | // 16 | // You should have received a copy of the GNU General Public License along 17 | // with this program; if not, write to the Free Software Foundation, Inc., 18 | // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 | // 20 | // http://www.gnu.org/licenses/gpl-2.0.txt 21 | /////////////////////////////////////////////////////////////////////////////// 22 | 23 | #pragma once 24 | 25 | #include 26 | 27 | //UIC forward declartion 28 | namespace Ui { 29 | class MainWindow; 30 | } 31 | 32 | class QLabel; 33 | class QMovie; 34 | class DirectoryScanner; 35 | class FileComparator; 36 | class DuplicatesModel; 37 | class QModelIndex; 38 | class QElapsedTimer; 39 | 40 | //MainWindow class 41 | class MainWindow: public QMainWindow 42 | { 43 | Q_OBJECT 44 | 45 | public: 46 | MainWindow(void); 47 | virtual ~MainWindow(void); 48 | 49 | private slots: 50 | void startScan(void); 51 | void directoryScannerFinished(void); 52 | void fileComparatorProgressChanged(const int &progress); 53 | void fileComparatorFinished(void); 54 | void openFile(void); 55 | void openFile(const QModelIndex &index); 56 | void gotoFile(void); 57 | void gotoFile(const QModelIndex &index); 58 | void renameFile(void); 59 | void renameFile(const QModelIndex &index); 60 | void deleteFile(void); 61 | void deleteFile(const QModelIndex &index); 62 | void clearData(void); 63 | void exportToFile(void); 64 | void copyToClipboard(void); 65 | void autoClean(void); 66 | void showHomepage(void); 67 | void showAbout(void); 68 | 69 | protected: 70 | virtual void closeEvent(QCloseEvent *e); 71 | virtual void resizeEvent(QResizeEvent *e); 72 | virtual void showEvent(QShowEvent *e); 73 | virtual void keyPressEvent(QKeyEvent *e); 74 | virtual void dragEnterEvent(QDragEnterEvent *e); 75 | virtual void dropEvent(QDropEvent *e); 76 | virtual bool winEvent(MSG *message, long *result); 77 | 78 | private: 79 | void centerWidget(QWidget *widget); 80 | QLabel *makeLabel(QWidget *parent, const QString &fileName, const bool &hidden = true); 81 | void updateProgress(const int &progress, const int &maxValue = 100); 82 | void setButtonsEnabled(const bool &enabled); 83 | void setMenuItemsEnabled(const bool &enabled); 84 | void showSign(const int &id); 85 | void handleCommandLineArgs(void); 86 | QModelIndex getSelectedItem(void); 87 | void togglePause(void); 88 | 89 | static QString cleanFileName(const QString &fileName); 90 | 91 | QLabel *m_animator; 92 | QMovie *m_movie; 93 | QElapsedTimer *m_timer; 94 | 95 | QLabel *m_signCompleted; 96 | QLabel *m_signCancelled; 97 | QLabel *m_signQuiescent; 98 | 99 | volatile bool m_runningFlag; 100 | volatile bool m_abortFlag; 101 | volatile bool m_pauseFlag; 102 | volatile bool m_unattendedFlag; 103 | 104 | QStringList m_droppedFolders; 105 | QString m_unpauseText; 106 | 107 | DuplicatesModel *m_model; 108 | DirectoryScanner *m_directoryScanner; 109 | FileComparator *m_fileComparator; 110 | 111 | Ui::MainWindow *const ui; 112 | }; 113 | -------------------------------------------------------------------------------- /src/strnatcmp/README.htm: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Natural Order String Comparison 5 | 6 | 7 | 8 |

Natural Order String Comparison

9 |

by Martin Pool 10 | 11 |

Computer string sorting algorithms generally don't order strings 12 | containing numbers in the same way that a human would do. Consider: 13 | 14 |

rfc1.txt
 15 | rfc2086.txt
 16 | rfc822.txt
 17 | 
18 |

It would be more friendly if the program listed the files as 19 | 20 |

rfc1.txt
 21 | rfc822.txt
 22 | rfc2086.txt
 23 | 
24 | 25 |

Filenames sort properly if people insert leading zeros, but they 26 | don't always do that. 27 | 28 |

I've written a subroutine that compares strings according to this 29 | natural ordering. You can use this routine in your own software, or 30 | download a patch to add it to your favourite Unix program. 31 | 32 | 33 |

Sorting

34 | 35 |

Strings are sorted as usual, except that decimal integer substrings 36 | are compared on their numeric value. For example, 37 | 38 |

39 | a < a0 < a1 < a1a < a1b < a2 < a10 < a20 40 |
41 | 42 |

Strings can contain several number parts: 43 | 44 |

45 | x2-g8 < x2-y7 < x2-y08 < x8-y8 46 |
47 | 48 | in which case numeric fields are separated by nonnumeric characters. 49 | Leading spaces are ignored. This works very well for IP addresses 50 | from log files, for example. 51 | 52 |

53 | Leading zeros are not ignored, which tends to give more 54 | reasonable results on decimal fractions. 55 |

56 | 57 |
58 | 1.001 < 1.002 < 1.010 < 1.02 < 1.1 < 1.3 59 |
60 | 61 |

Some applications may wish to change this by modifying the test 62 | that calls isspace. 63 | 64 | 65 |

66 | Performance is linear: each character of the string is scanned 67 | at most once, and only as many characters as necessary to decide 68 | are considered. 69 |

70 | 71 |

Longer example of the results 72 | 73 | 74 |

Licensing

75 | 76 |

This software is copyright by Martin Pool, and made available under 77 | the same licence as zlib: 78 | 79 |

80 |

This software is provided 'as-is', without any express or implied 81 | warranty. In no event will the authors be held liable for any damages 82 | arising from the use of this software. 83 | 84 |

Permission is granted to anyone to use this software for any purpose, 85 | including commercial applications, and to alter it and redistribute it 86 | freely, subject to the following restrictions: 87 | 88 |

1. The origin of this software must not be misrepresented; you must not 89 | claim that you wrote the original software. If you use this software 90 | in a product, an acknowledgment in the product documentation would be 91 | appreciated but is not required. 92 |

2. Altered source versions must be plainly marked as such, and must not be 93 | misrepresented as being the original software. 94 |

3. This notice may not be removed or altered from any source distribution. 95 |

96 | 97 |

This licence applies only to the C implementation. You are free to 98 | reimplement the idea fom scratch in any language. 99 | 100 |

Related Work

101 | 102 | 103 |

104 | POSIX sort(1) has the -n option to sort numbers, but this doesn't 105 | work if there is a non-numeric prefix. 106 |

107 | 108 |

109 | GNU ls(1) has the --sort=version option, which works 110 | the same way. 111 |

112 | 113 |

114 | The PHP scripting language now has a 115 | strnatcmp 116 | function based on this code. 117 | The PHP wrapper was done by Andrei Zimievsky. 118 |

119 | 120 |

121 | Stuart 122 | Cheshire has a Macintosh system extension to do natural ordering. 123 | I indepdendently reinvented the algorithm, but Stuart had it 124 | first. I borrowed the term natural sort from him. 125 | 126 |

127 | 128 |

129 | Sort::Versions 130 | in Perl. "The code has some special magic to deal with common 131 | conventions in program version numbers, like the difference between 132 | 'decimal' versions (eg perl 5.005) and the Unix kind (eg perl 5.6.1)." 133 | 134 |

Sort::Naturally 135 | is also in Perl, by Sean M. Burke. It uses locale-sensitive character classes to sort words and numeric substrings 136 | in a way similar to natsort. 137 | 138 |

139 | Ed Avis wrote something similar in Haskell. 140 | 141 | 142 |

143 | Pierre-Luc Paour wrote a NaturalOrderComparator 144 | in Java 145 | 146 |

Kristof Coomans wrote a natural sort comparison in Javascript

147 | 148 |

Alan Davies wrote 149 | natcmp.rb, 150 | an implementation in Ruby. 151 | 152 |

Numacomp 153 | - similar thing in Python. 154 | 155 |

as3natcompare 156 | implementation in Flash ActionScript 3. 157 | 158 |

Get It!

159 | 160 |
    161 |
  • strnatcmp.c, 162 | strnatcmp.h - the algorithm itself 163 | 164 |
  • natsort.c - example driver program. 165 | (Try ls -F /proc | natsort) 166 | 167 |
  • textutils.diff - patch to add 168 | natural sort to sort(1) from GNU textutils-2.0; use the new 169 | -N option.
  • 170 | 171 |
  • Natural ordering is now in PHP4rc2, through the strnatcasecmp 172 | and strnatcmp 173 | functions.
  • 174 |
175 | 176 | 177 |

To Do

178 | 179 |

180 | Comparison of characters is purely numeric, without taking 181 | character set or locale into account. So it is only correct for 182 | ASCII. This should probably be a separate function because doing 183 | the comparisons will probably introduce a dependency on the OS 184 | mechanism for finding the locale and comparing characters. 185 | 186 | 187 |

188 | It might be good to support multibyte character sets too. 189 | 190 |

191 | If you fix either of these, please mail me. They should not be 192 | very hard. 193 | 194 | 195 | 196 |

-------------------------------------------------------------------------------- /src/strnatcmp/strnatcmp.cpp: -------------------------------------------------------------------------------- 1 | /* -*- mode: c; c-file-style: "k&r" -*- 2 | 3 | strnatcmp.c -- Perform 'natural order' comparisons of strings in C. 4 | Copyright (C) 2000, 2004 by Martin Pool 5 | 6 | This software is provided 'as-is', without any express or implied 7 | warranty. In no event will the authors be held liable for any damages 8 | arising from the use of this software. 9 | 10 | Permission is granted to anyone to use this software for any purpose, 11 | including commercial applications, and to alter it and redistribute it 12 | freely, subject to the following restrictions: 13 | 14 | 1. The origin of this software must not be misrepresented; you must not 15 | claim that you wrote the original software. If you use this software 16 | in a product, an acknowledgment in the product documentation would be 17 | appreciated but is not required. 18 | 2. Altered source versions must be plainly marked as such, and must not be 19 | misrepresented as being the original software. 20 | 3. This notice may not be removed or altered from any source distribution. 21 | */ 22 | 23 | 24 | /* partial change history: 25 | * 26 | * 2004-10-10 mbp: Lift out character type dependencies into macros. 27 | * 28 | * Eric Sosman pointed out that ctype functions take a parameter whose 29 | * value must be that of an unsigned int, even on platforms that have 30 | * negative chars in their default char type. 31 | */ 32 | 33 | /* 34 | * 2013-08-23: Skip leading zero's for any run of digits, except 35 | * when a decimal point was seen immediatley before. 36 | * Patch by LoRd_MuldeR 37 | */ 38 | 39 | #include 40 | #include 41 | #include 42 | #include 43 | 44 | #include "strnatcmp.h" 45 | 46 | /* These are defined as macros to make it easier to adapt this code to 47 | * different characters types or comparison functions. */ 48 | static inline int nat_isdigit(nat_char a) 49 | { 50 | return iswdigit(a); 51 | } 52 | 53 | static inline int nat_isspace(nat_char a) 54 | { 55 | return iswspace(a); 56 | } 57 | 58 | static inline nat_char nat_isdecpoint(nat_char a) 59 | { 60 | return (a == L'.') || (a == L','); 61 | } 62 | 63 | static inline nat_char nat_toupper(nat_char a) 64 | { 65 | return towupper(a); 66 | } 67 | 68 | static int compare_right(nat_char const *a, nat_char const *b) 69 | { 70 | int bias = 0; 71 | 72 | /* The longest run of digits wins. That aside, the greatest 73 | value wins, but we can't know that it will until we've scanned 74 | both numbers to know that they have the same magnitude, so we 75 | remember it in BIAS. */ 76 | for (;; a++, b++) 77 | { 78 | if (!nat_isdigit(*a) && !nat_isdigit(*b)) 79 | return bias; 80 | else if (!nat_isdigit(*a)) 81 | return -1; 82 | else if (!nat_isdigit(*b)) 83 | return +1; 84 | else if (*a < *b) 85 | { 86 | if (!bias) 87 | bias = -1; 88 | } 89 | else if (*a > *b) 90 | { 91 | if (!bias) 92 | bias = +1; 93 | } 94 | else if (!*a && !*b) 95 | return bias; 96 | } 97 | 98 | return 0; 99 | } 100 | 101 | static int compare_left(nat_char const *a, nat_char const *b) 102 | { 103 | /* Compare two left-aligned numbers: the first to have a 104 | different value wins. */ 105 | for (;; a++, b++) 106 | { 107 | if (!nat_isdigit(*a) && !nat_isdigit(*b)) 108 | return 0; 109 | else if (!nat_isdigit(*a)) 110 | return -1; 111 | else if (!nat_isdigit(*b)) 112 | return +1; 113 | else if (*a < *b) 114 | return -1; 115 | else if (*a > *b) 116 | return +1; 117 | } 118 | 119 | return 0; 120 | } 121 | 122 | static int strnatcmp0(nat_char const *a, nat_char const *b, const bool fold_case) 123 | { 124 | int ai, bi; 125 | nat_char ca, cb; 126 | int result; 127 | bool fractional; 128 | int sa, sb; 129 | 130 | assert(a && b); 131 | ai = bi = 0; 132 | fractional = false; 133 | 134 | while (1) 135 | { 136 | ca = a[ai]; cb = b[bi]; 137 | 138 | /* skip over leading spaces or zeros */ 139 | while (nat_isspace(ca)) 140 | ca = a[++ai]; 141 | 142 | while (nat_isspace(cb)) 143 | cb = b[++bi]; 144 | 145 | /* process run of digits */ 146 | if (nat_isdigit(ca) && nat_isdigit(cb)) 147 | { 148 | sa = sb = 0; 149 | 150 | if(!fractional) 151 | { 152 | while (ca == L'0') 153 | { 154 | ca = a[++ai]; sa++; 155 | } 156 | while (cb == L'0') 157 | { 158 | cb = b[++bi]; sb++; 159 | } 160 | } 161 | 162 | if (fractional) 163 | { 164 | if ((result = compare_left(a+ai, b+bi)) != 0) 165 | return result; 166 | } 167 | else 168 | { 169 | if ((result = compare_right(a+ai, b+bi)) != 0) 170 | return result; 171 | } 172 | 173 | /* on tie, the string with the longer leading zero's sequence wins */ 174 | if(sa < sb) 175 | return -1; 176 | else if(sa > sb) 177 | return +1; 178 | } 179 | 180 | if (!ca && !cb) 181 | { 182 | /* The strings compare the same. Perhaps the caller 183 | will want to call strcmp to break the tie. */ 184 | return (fold_case) ? _wcsicmp(a, b) : wcscmp(a, b); 185 | } 186 | 187 | if (fold_case) 188 | { 189 | ca = nat_toupper(ca); 190 | cb = nat_toupper(cb); 191 | } 192 | 193 | if (ca < cb) 194 | return -1; 195 | else if (ca > cb) 196 | return +1; 197 | 198 | /* skipp leading zero's, unless previously seen char was a decimal point */ 199 | fractional = nat_isdecpoint(ca) && nat_isdecpoint(cb); 200 | 201 | ++ai; ++bi; 202 | } 203 | } 204 | 205 | int strnatcmp(nat_char const *a, nat_char const *b) 206 | { 207 | return strnatcmp0(a, b, false); 208 | } 209 | 210 | /* Compare, recognizing numeric string and ignoring case. */ 211 | int strnatcasecmp(nat_char const *a, nat_char const *b) 212 | { 213 | return strnatcmp0(a, b, true); 214 | } 215 | -------------------------------------------------------------------------------- /src/strnatcmp/strnatcmp.h: -------------------------------------------------------------------------------- 1 | /* -*- mode: c; c-file-style: "k&r" -*- 2 | 3 | strnatcmp.c -- Perform 'natural order' comparisons of strings in C. 4 | Copyright (C) 2000, 2004 by Martin Pool 5 | 6 | This software is provided 'as-is', without any express or implied 7 | warranty. In no event will the authors be held liable for any damages 8 | arising from the use of this software. 9 | 10 | Permission is granted to anyone to use this software for any purpose, 11 | including commercial applications, and to alter it and redistribute it 12 | freely, subject to the following restrictions: 13 | 14 | 1. The origin of this software must not be misrepresented; you must not 15 | claim that you wrote the original software. If you use this software 16 | in a product, an acknowledgment in the product documentation would be 17 | appreciated but is not required. 18 | 2. Altered source versions must be plainly marked as such, and must not be 19 | misrepresented as being the original software. 20 | 3. This notice may not be removed or altered from any source distribution. 21 | */ 22 | 23 | 24 | /* CUSTOMIZATION SECTION 25 | * 26 | * You can change this typedef, but must then also change the inline 27 | * functions in strnatcmp.c */ 28 | typedef wchar_t nat_char; 29 | 30 | int strnatcmp(nat_char const *a, nat_char const *b); 31 | int strnatcasecmp(nat_char const *a, nat_char const *b); 32 | -------------------------------------------------------------------------------- /z_build.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | REM /////////////////////////////////////////////////////////////////////////// 3 | REM // Set Paths 4 | REM /////////////////////////////////////////////////////////////////////////// 5 | set "MSVC_PATH=C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC" 6 | set "QTVC_PATH=C:\Qt\4.8.7" 7 | 8 | REM ############################################### 9 | REM # DO NOT MODIFY ANY LINES BELOW THIS LINE !!! # 10 | REM ############################################### 11 | 12 | 13 | REM /////////////////////////////////////////////////////////////////////////// 14 | REM // Setup environment 15 | REM /////////////////////////////////////////////////////////////////////////// 16 | if exist "%QTVC_PATH%\bin\qtvars.bat" ( call "%QTVC_PATH%\bin\qtvars.bat" ) 17 | if exist "%QTVC_PATH%\bin\qtenv2.bat" ( call "%QTVC_PATH%\bin\qtenv2.bat" ) 18 | call "%MSVC_PATH%\vcvarsall.bat" x86 19 | 20 | REM /////////////////////////////////////////////////////////////////////////// 21 | REM // Check environment 22 | REM /////////////////////////////////////////////////////////////////////////// 23 | if "%VCINSTALLDIR%"=="" ( 24 | echo %%VCINSTALLDIR%% not specified. Please check your MSVC_PATH var! 25 | goto BuildError 26 | ) 27 | if "%QTDIR%"=="" ( 28 | echo %%QTDIR%% not specified. Please check your MSVC_PATH var! 29 | goto BuildError 30 | ) 31 | if not exist "%VCINSTALLDIR%\bin\cl.exe" ( 32 | echo C++ compiler not found. Please check your MSVC_PATH var! 33 | goto BuildError 34 | ) 35 | if not exist "%QTDIR%\bin\moc.exe" ( 36 | echo Qt meta compiler not found. Please check your QTVC_PATH var! 37 | goto BuildError 38 | ) 39 | 40 | REM /////////////////////////////////////////////////////////////////////////// 41 | REM // Get current date and time (in ISO format) 42 | REM /////////////////////////////////////////////////////////////////////////// 43 | set "ISO_DATE=" 44 | set "ISO_TIME=" 45 | if not exist "%~dp0\..\Prerequisites\GnuWin32\date.exe" BuildError 46 | for /F "tokens=1,2 delims=:" %%a in ('"%~dp0\..\Prerequisites\GnuWin32\date.exe" +ISODATE:%%Y-%%m-%%d') do ( 47 | if "%%a"=="ISODATE" set "ISO_DATE=%%b" 48 | ) 49 | for /F "tokens=1,2,3,4 delims=:" %%a in ('"%~dp0\..\Prerequisites\GnuWin32\date.exe" +ISOTIME:%%T') do ( 50 | if "%%a"=="ISOTIME" set "ISO_TIME=%%b:%%c:%%d" 51 | ) 52 | if "%ISO_DATE%"=="" goto BuildError 53 | if "%ISO_TIME%"=="" goto BuildError 54 | 55 | REM /////////////////////////////////////////////////////////////////////////// 56 | REM // Build the binaries 57 | REM /////////////////////////////////////////////////////////////////////////// 58 | echo --------------------------------------------------------------------- 59 | echo BEGIN BUILD 60 | echo --------------------------------------------------------------------- 61 | MSBuild.exe /property:Configuration=Release_Static /target:clean "%~dp0\DoubleFileScanner.sln" 62 | if not "%ERRORLEVEL%"=="0" goto BuildError 63 | MSBuild.exe /property:Configuration=Release_Static /target:rebuild "%~dp0\DoubleFileScanner.sln" 64 | if not "%ERRORLEVEL%"=="0" goto BuildError 65 | MSBuild.exe /property:Configuration=Release_Static /target:build "%~dp0\DoubleFileScanner.sln" 66 | if not "%ERRORLEVEL%"=="0" goto BuildError 67 | 68 | REM /////////////////////////////////////////////////////////////////////////// 69 | REM // Copy base files 70 | REM /////////////////////////////////////////////////////////////////////////// 71 | echo --------------------------------------------------------------------- 72 | echo BEGIN PACKAGING 73 | echo --------------------------------------------------------------------- 74 | set "PACK_PATH=%TMP%\~%RANDOM%%RANDOM%.tmp" 75 | mkdir "%PACK_PATH%" 76 | copy "%~dp0\bin\Win32\Release_Static\*.exe" "%PACK_PATH%" 77 | copy "%~dp0\*.txt" "%PACK_PATH%" 78 | copy "%~dp0\*.html" "%PACK_PATH%" 79 | 80 | 81 | REM /////////////////////////////////////////////////////////////////////////// 82 | REM // Compress 83 | REM /////////////////////////////////////////////////////////////////////////// 84 | "%~dp0\..\Prerequisites\UPX\upx.exe" --best "%PACK_PATH%\*.exe" 85 | 86 | REM /////////////////////////////////////////////////////////////////////////// 87 | REM // Attributes 88 | REM /////////////////////////////////////////////////////////////////////////// 89 | attrib +R "%PACK_PATH%\*.exe" 90 | attrib +R "%PACK_PATH%\*.html" 91 | attrib +R "%PACK_PATH%\*.txt" 92 | 93 | REM /////////////////////////////////////////////////////////////////////////// 94 | REM // Generate outfile name 95 | REM /////////////////////////////////////////////////////////////////////////// 96 | mkdir "%~dp0\out" 97 | set "OUT_NAME=DoubleFileScanner.%ISO_DATE%" 98 | :CheckOutName 99 | if exist "%~dp0\out\%OUT_NAME%.zip" ( 100 | set "OUT_NAME=%OUT_NAME%.new" 101 | goto CheckOutName 102 | ) 103 | 104 | REM /////////////////////////////////////////////////////////////////////////// 105 | REM // Create version tag 106 | REM /////////////////////////////////////////////////////////////////////////// 107 | echo Double File Scanner > "%~dp0\out\%OUT_NAME%.txt" 108 | echo Copyright (C) 2017 LoRd_MuldeR ^ >> "%~dp0\out\%OUT_NAME%.txt" 109 | echo Built %ISO_DATE%, %TIME% >> "%~dp0\out\%OUT_NAME%.txt" 110 | echo. >> "%~dp0\out\%OUT_NAME%.txt" 111 | echo This program is free software; you can redistribute it and/or modify >> "%~dp0\out\%OUT_NAME%.txt" 112 | echo it under the terms of the GNU General Public License as published by >> "%~dp0\out\%OUT_NAME%.txt" 113 | echo the Free Software Foundation; either version 2 of the License, or >> "%~dp0\out\%OUT_NAME%.txt" 114 | echo (at your option) any later version. >> "%~dp0\out\%OUT_NAME%.txt" 115 | 116 | REM /////////////////////////////////////////////////////////////////////////// 117 | REM // Build the package 118 | REM /////////////////////////////////////////////////////////////////////////// 119 | pushd "%PACK_PATH% 120 | "%~dp0\..\Prerequisites\GnuWin32\zip.exe" -9 -r -z "%~dp0\out\%OUT_NAME%.zip" "*.*" < "%~dp0\out\%OUT_NAME%.txt" 121 | popd 122 | rmdir /Q /S "%PACK_PATH%" 123 | attrib +R "%~dp0\out\%OUT_NAME%.zip" 124 | del "%~dp0\out\%OUT_NAME%.txt" 125 | 126 | REM /////////////////////////////////////////////////////////////////////////// 127 | REM // COMPLETE 128 | REM /////////////////////////////////////////////////////////////////////////// 129 | echo. 130 | echo Build completed. 131 | echo. 132 | pause 133 | goto:eof 134 | 135 | REM /////////////////////////////////////////////////////////////////////////// 136 | REM // FAILED 137 | REM /////////////////////////////////////////////////////////////////////////// 138 | :BuildError 139 | echo. 140 | echo Build has failed !!! 141 | echo. 142 | pause 143 | -------------------------------------------------------------------------------- /z_drMemory.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | set "DRMEM=D:\Dr. Memory" 3 | 4 | :: ----------------------------------------------------------------- 5 | 6 | set "PATH=%QTDIR%\bin;%PATH%" 7 | 8 | pushd "%~dp0\bin\Win32\Debug" 9 | "%~dp0\etc\inifile.exe" .\vld.ini [Options] VLD=off 10 | 11 | "%DRMEM%\bin\drmemory.exe" -suppress "%~dp0\z_drMemory.spr" -- ".\DoubleFileScanner.exe" 12 | 13 | "%~dp0\etc\inifile.exe" .\vld.ini [Options] VLD=on 14 | popd 15 | 16 | :: ----------------------------------------------------------------- 17 | 18 | echo. 19 | pause 20 | -------------------------------------------------------------------------------- /z_drMemory.spr: -------------------------------------------------------------------------------- 1 | UNINITIALIZED READ 2 | QtCored4.dll!* 3 | 4 | UNINITIALIZED READ 5 | QtGuid4.dll!* 6 | --------------------------------------------------------------------------------