From 8cd0ef4c2b447a61ac1b2cd41aab020b40ab2194 Mon Sep 17 00:00:00 2001 From: Andrew Senetar Date: Mon, 28 Aug 2017 18:27:17 -0500 Subject: [PATCH 1/7] Initial Update of Windows Packaging (#438) * Update run.py & .gitignore for windows - Update run.py to execute on windows as SIGQUIT is not available. - Update .gitignore to ignore the generate .pyd files Ref #300, #393 * Update package.py for windows Add package_windows back into package.py - Using cx_freeze for freezing installation - Will be using nsis for actual installer Tested with python 3.5 64bit on windows 10 Ref #393 * Update makefile for windows (+2 misc) - Update the makefile to support windows - Use different bin path in virtualenv - Use pyd instead of so files - Tested with Msys2 - Add *.exe to .gitignore - Fix minor format error in package.py Ref #393 * Add requirements-windows Add the requirements-windows.txt - contains cx-Freeze for bundling Ref #393 * Add initial setup.nsi Initial Version of a NSIS installer script - Multi-user install (install for just one or all) - Registers uninstaller (more values need to finish up) - Tested both single and all install / uninstall and works - Still need to add parameters instead of hardcoded values in some spots - Need to clean up vendor folders / keys if empty on uninstall - Need to add the other dupeGuru languages to the language list - Minor cleanup of script needed as well Ref #393 * Update setup.nsi Updates to setup.nsi including: - Defines from CLI - Version information (MAJOR, MINOR, PATCH) - Bits (64 / 32) - SourcePath (if we wanted something other than build) - Added extra defines to move application specifics to one location - Added extra defines for uninstall information - Added calculation of install size - Added switching between 64 and 32 bit contexts (need to verify functionality) - Updated output file naming - Added NSIS supported languages which are also supported by dupeGuru - Added rest of registry keys for uninstall information - Added missing registry key for installType - Added removeing Vendor folder and registry key if empty on uninstall Should be very close to having this installer script ready to integrate into the package.py script if desired. Ref #393 * Update README & requirements-windows Minor update to README to indicate windows is supported. Add PyQt5 to requirements-windows.txt to make installation easier. * Update packaging for windows - Update package.py to integrate NSIS for windows - Update makefile to deal with a few additional windows issues - Add Windows.md to contain specific windows instructions, if we want this can be merged with README.md - Minor formatting update to setup.nsi Ref #393 * Update README & Windows Instructions - Update the README to include a reference to the Windows instructions. - Add some additional notes into Windows Instructions and remove one incorrect command. - Update .gitignore to ignore all permutations of env* to allow for multiple side by side virtual environments (used to build different versions for windows) Ref: #393 * Update Window.md Fix broken python link and move nsis link for consistency. * More Details in Windows.md Update Windows.md including: - Information on compilier requirements for windows - Notes about the windows 10 sdk - Some clarification around some of the steps - Addition of msys2 links Going to review this a bit more to polish it up. Ref #393. --- .gitignore | 5 +- Makefile | 38 ++++-- README.md | 5 +- Windows.md | 59 +++++++++ package.py | 84 +++++++++++-- requirements-windows.txt | 2 + run.py | 7 +- setup.nsi | 260 +++++++++++++++++++++++++++++++++++++++ 8 files changed, 438 insertions(+), 22 deletions(-) create mode 100644 Windows.md create mode 100644 requirements-windows.txt create mode 100644 setup.nsi diff --git a/.gitignore b/.gitignore index f66b475c..439b333d 100644 --- a/.gitignore +++ b/.gitignore @@ -9,7 +9,7 @@ __pycache__ build dist -env +env* /deps cocoa/autogen @@ -19,3 +19,6 @@ cocoa/autogen /qt/*_rc.py /help/*/conf.py /help/*/changelog.rst + +*.pyd +*.exe \ No newline at end of file diff --git a/Makefile b/Makefile index 47aae5c8..b8ccc50b 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,23 @@ PYTHON ?= python3 +PYRCC5 ?= pyrcc5 REQ_MINOR_VERSION = 4 PREFIX ?= /usr/local +# Window compatability via Msys2 +# - venv creates Scripts instead of bin +# - compile generates .pyd instead of .so +# - venv with --sytem-site-packages has issues on windows as well... + +ifeq ($(shell uname -o), Msys) + BIN = Scripts + SO = pyd + VENV_OPTIONS = +else + BIN = bin + SO = so + VENV_OPTIONS = --system-site-packages +endif + # Set this variable if all dependencies are already met on the system. We will then avoid the # whole vitualenv creation and pip install dance. NO_VENV ?= @@ -9,7 +25,7 @@ NO_VENV ?= ifdef NO_VENV VENV_PYTHON = $(PYTHON) else - VENV_PYTHON = ./env/bin/python + VENV_PYTHON = ./env/$(BIN)/python endif # If you're installing into a path that is not going to be the final path prefix (such as a @@ -61,33 +77,33 @@ ifndef NO_VENV $(VENV_PYTHON) -m pip install -r requirements.txt # We can't use the "--system-site-packages" flag on creation because otherwise we end up with # the system's pip and that messes up things in some cases (notably in Gentoo). - ${PYTHON} -m venv --upgrade --system-site-packages env + ${PYTHON} -m venv --upgrade ${VENV_OPTIONS} env endif build/help : | env $(VENV_PYTHON) build.py --doc qt/dg_rc.py : qt/dg.qrc - pyrcc5 qt/dg.qrc > qt/dg_rc.py + $(PYRCC5) qt/dg.qrc > qt/dg_rc.py i18n: $(mofiles) %.mo : %.po msgfmt -o $@ $< -core/pe/_block.*.so : core/pe/modules/block.c core/pe/modules/common.c | env +core/pe/_block.*.$(SO) : core/pe/modules/block.c core/pe/modules/common.c | env $(VENV_PYTHON) hscommon/build_ext.py $^ _block - mv _block.*.so core/pe + mv _block.*.$(SO) core/pe -core/pe/_cache.*.so : core/pe/modules/cache.c core/pe/modules/common.c | env +core/pe/_cache.*.$(SO) : core/pe/modules/cache.c core/pe/modules/common.c | env $(VENV_PYTHON) hscommon/build_ext.py $^ _cache - mv _cache.*.so core/pe + mv _cache.*.$(SO) core/pe -qt/pe/_block_qt.*.so : qt/pe/modules/block.c | env +qt/pe/_block_qt.*.$(SO) : qt/pe/modules/block.c | env $(VENV_PYTHON) hscommon/build_ext.py $^ _block_qt - mv _block_qt.*.so qt/pe + mv _block_qt.*.$(SO) qt/pe -modules : core/pe/_block.*.so core/pe/_cache.*.so qt/pe/_block_qt.*.so +modules : core/pe/_block.*.$(SO) core/pe/_cache.*.$(SO) qt/pe/_block_qt.*.$(SO) mergepot : $(VENV_PYTHON) build.py --mergepot @@ -123,6 +139,6 @@ uninstall : clean: -rm -rf build -rm locale/*/LC_MESSAGES/*.mo - -rm core/pe/*.so qt/pe/*.so + -rm core/pe/*.$(SO) qt/pe/*.$(SO) .PHONY : clean srcpkg normpo mergepot modules i18n reqs run pyc install uninstall all diff --git a/README.md b/README.md index a1e7c6da..6697c21f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # dupeGuru -[dupeGuru][dupeguru] is a cross-platform (Linux and OS X) GUI tool to find duplicate files in +[dupeGuru][dupeguru] is a cross-platform (Linux, OS X, Windows) GUI tool to find duplicate files in a system. It's written mostly in Python 3 and has the peculiarity of using [multiple GUI toolkits][cross-toolkit], all using the same core Python code. On OS X, the UI layer is written in Objective-C and uses Cocoa. On Linux, it's written in Python and uses Qt5. @@ -66,6 +66,9 @@ git submodules: ## How to build dupeGuru from source +### Windows +For windows instructions see the [Windows Instructions](Windows.md). + ### Prerequisites * [Python 3.4+][python] diff --git a/Windows.md b/Windows.md new file mode 100644 index 00000000..74295e1a --- /dev/null +++ b/Windows.md @@ -0,0 +1,59 @@ +## How to build dupeGuru for Windows + +### Prerequisites + +- [Python 3.5+][python] +- [Visual Studio 2017][vs] or [Visual Studio Build Tools 2017][vsBuildTools] with the Windows 10 SDK +- [nsis][nsis] (for installer creation) +- [msys2][msys2] (for using makefile method) + +When installing Visual Studio or the Visual Studio Build Tools with the Windows 10 SDK on versions of Windows below 10 be sure to make sure that the Universal CRT is installed before installing Visual studio as noted in the [Windows 10 SDK Notes][win10sdk] and found at [KB2999226][KB2999226]. + +After installing python it is recommended to update setuptools before compiling packages. To update run (example is for python launcher and 3.5): + + $ py -3.5 -m pip install --upgrade setuptools + +More details on setting up python for compiling packages on windows can be found on the [python wiki][pythonWindowsCompilers] + +### With build.py (preferred) +To build with a different python version 3.5 vs 3.6 or 32 bit vs 64 bit specify that version instead of -3.5 to the `py` command below. If you want to build additional versions while keeping all virtual environments setup use a different location for each vritual environment. + + $ cd + $ git submodule init + $ git submodule update + $ py -3.5 -m venv .\env + $ .\env\Scripts\activate + $ pip install -r requirements.txt -r requirements-windows.txt + $ python build.py + $ python run.py + +### With makefile +It is possible to build dupeGuru with the makefile on windows using a compatable POSIX environment. The following steps have been tested using [msys2][msys2]. Before running make: +1. Install msys2 or other POSIX environment +2. Install PyQt5 globally via pip +3. Use the respective console for msys2 it is `msys2 msys` + +Then the following execution of the makefile should work. Pass the correct value for PYTHON to the makefile if not on the path as python3. + + $ cd + $ make PYTHON='py -3.5' + $ make run + +NOTE: Install PyQt5 & cx-Freeze with requirements-windows.txt into the venv before runing the packaging scripts in the section below. + +### Generate Windows Installer Packages +You need to use the respective x86 or x64 version of python to build the 32 bit and 64 bit versions. The build scripts will automatically detect the python architecture for you. When using build.py make sure the resulting python works before continuing to package.py. NOTE: package.py looks for the 'makensis' executable in the default location for a 64 bit windows system. Run the following in the respective virtual environment. + + $ python package.py + +### Running tests +The complete test suite can be run with tox just like on linux. + +[python]: http://www.python.org/ +[nsis]: http://nsis.sourceforge.net/Main_Page +[vs]: https://www.visualstudio.com/downloads/#visual-studio-community-2017 +[vsBuildTools]: https://www.visualstudio.com/downloads/#build-tools-for-visual-studio-2017 +[win10sdk]: https://developer.microsoft.com/en-us/windows/downloads/windows-10-sdk +[KB2999226]: https://support.microsoft.com/en-us/help/2999226/update-for-universal-c-runtime-in-windows +[pythonWindowsCompilers]: https://wiki.python.org/moin/WindowsCompilers +[msys2]: http://www.msys2.org/ diff --git a/package.py b/package.py index 78e90abe..cca98b12 100644 --- a/package.py +++ b/package.py @@ -4,6 +4,7 @@ # which should be included with this package. The terms are also available at # http://www.gnu.org/licenses/gpl-3.0.html +import sys import os import os.path as op import compileall @@ -11,6 +12,7 @@ import shutil import json from argparse import ArgumentParser import platform +import re from hscommon.build import ( print_and_do, copy_packages, build_debian_changelog, @@ -112,6 +114,70 @@ def package_source_tgz(): print_and_do('tar -A {} -f {}'.format(archive_path, dest)) print_and_do('gzip {}'.format(dest)) +def package_windows(): + from cx_Freeze import setup, Executable + app_version = get_module_version('core') + arch = platform.architecture()[0] + buildpath = op.join('build', 'dupeguru-win{}'.format(arch)) + # remove existing build directory + if op.exists(buildpath): + shutil.rmtree(buildpath) + include_files = [] + # include locale files if they are built otherwise exit as it will break + # the localization + if op.exists('build/locale'): + include_files.append(('build/locale', 'locale')) + else: + print("Locale files not built, exiting...") + return + # include help files if they are built otherwise exit as they should be included? + if op.exists('build/help'): + include_files.append(('build/help', 'help')) + else: + print("Help files not built, exiting...") + return + # options for cx_Freeze + # if zip_include packages is not used, the cx_Freeze packager will include + # the whole PyQT5 directory + options = { + 'build_exe': { + 'build_exe': buildpath, + 'excludes': [], + 'includes': ['atexit'], + 'include_files': include_files, + 'include_msvcr': True, + 'zip_include_packages': ['*'], + 'zip_exclude_packages': [] + }, + } + # executables to build, uses se edition icon + executables = [ + Executable( + script='run.py', + base='Win32GUI', + targetName='dupeguru.exe', + icon='images/dgse_logo.ico', + copyright='Copyright (C) 2017 Hardcoded Software' + ) + ] + # call cx_freeze + setup( + name='dupeguru', + version=app_version, + description='Tool to find duplicate files on your computer.', + options=options, + executables=executables, + script_args=['build'] + ) + # Information to pass to NSIS + version_array = app_version.split('.') + match = re.search('[0-9]+', arch) + bits = match.group(0) + # Call NSIS (TODO update to not use hardcoded path) + cmd = ('"C:\\Program Files (x86)\\NSIS\\Bin\\makensis.exe" ' + '/DVERSIONMAJOR={0} /DVERSIONMINOR={1} /DVERSIONPATCH={2} /DBITS={3} setup.nsi') + print_and_do(cmd.format(version_array[0], version_array[1], version_array[2], bits)) + def main(): args = parse_args() if args.src_pkg: @@ -119,15 +185,17 @@ def main(): package_source_tgz() return print("Packaging dupeGuru with UI qt") - if not args.arch_pkg: - distname, _, _ = platform.dist() + if sys.platform == 'win32': + package_windows() else: - distname = 'arch' - if distname == 'arch': - package_arch() - else: - package_debian() + if not args.arch_pkg: + distname, _, _ = platform.dist() + else: + distname = 'arch' + if distname == 'arch': + package_arch() + else: + package_debian() if __name__ == '__main__': main() - diff --git a/requirements-windows.txt b/requirements-windows.txt new file mode 100644 index 00000000..ebd54dc4 --- /dev/null +++ b/requirements-windows.txt @@ -0,0 +1,2 @@ +PyQt5 >=5.4,<6.0 +cx-Freeze>=5.0.2,<6.0.0 diff --git a/run.py b/run.py index 6dfcf752..297a5bd8 100644 --- a/run.py +++ b/run.py @@ -20,7 +20,12 @@ from qt import dg_rc from qt.platform import BASE_PATH from core import __version__, __appname__ -from signal import signal, SIGINT, SIGTERM, SIGQUIT +# SIGQUIT is not defined on Windows +if sys.platform == 'win32': + from signal import signal, SIGINT, SIGTERM + SIGQUIT = SIGTERM +else: + from signal import signal, SIGINT, SIGTERM, SIGQUIT global dgapp dgapp = None diff --git a/setup.nsi b/setup.nsi new file mode 100644 index 00000000..ce2f3d84 --- /dev/null +++ b/setup.nsi @@ -0,0 +1,260 @@ +;============================================================================== +; dupeGuru Installer Script for Windows via NSIS +; +; When calling makensis use the following: +; makensis /DVERSIONMAJOR=x /DVERSIONMINOR=x /DVERSIONPATCH=x /DBITS=x \ +; /DSOURCEPATH=x +; NOTE: +; If SOURCEPATH is not set it will default to build (uses subdir based on app). +;============================================================================== + +; Compression Setting +SetCompressor /SOLID lzma +; General Headers +!include "FileFunc.nsh" + +;============================================================================== +; Configuration Defines +;============================================================================== + +; Environment Defines +!verbose push +!verbose 4 +!ifndef VERSIONMAJOR + !echo "VERSIONMAJOR is NOT defined" +!endif +!ifndef VERSIONMINOR + !echo "VERSIONMINOR is NOT defined" +!endif +!ifndef VERSIONPATCH + !echo "VERSIONPATCH is NOT defined" +!endif +!ifndef BITS + !echo "BITS is NOT defined" +!endif +!ifndef SOURCEPATH + !echo "SOURCEPATH is NOT defined" + !define SOURCEPATH "build" +!endif +!ifndef VERSIONMAJOR | VERSIONMINOR | VERSIONPATCH | BITS | SOURCEPATH + !error "Command line Defines missing use /DDEFINE=VALUE to define before script" +!endif +!verbose pop + +; Application Specific Defines +!define APPNAME "dupeGuru" +!define COMPANYNAME "Hardcoded Software" +!define DESCRIPTION "dupeGuru is a tool to find duplicate files on your computer." +!define APPLICENSE "LICENSE" ; License is not in build directory +!define APPICON "images\dgse_logo.ico" ; nor is the icon +!define DISTDIR "dist" +!define HELPURL "http://www.hardcoded.net/support/" +!define UPDATEURL "http://www.hardcoded.net/dupeguru/" +!define ABOUTURL "http://www.hardcoded.net/dupeguru/" + +; Static Defines +!define UNINSTALLREGBASE "Software\Microsoft\Windows\CurrentVersion\Uninstall" + +; Derived Defines +!define BASEREGKEY "Software\${COMPANYNAME}\${APPNAME}" ;without root key +!define VENDORREGKEY "Software\${COMPANYNAME}" ;without root key +!define UNINSTALLREG "${UNINSTALLREGBASE}\${APPNAME}" ;without root key +!define INSTPATH "${COMPANYNAME}\${APPNAME}" ;without programs / appdata + +; Global vars +var StartMenuFolder +var InstallSize + +;============================================================================== +; Plugin Setup +;============================================================================== + +; MultiUser Plugin - Allow single user or all install based on execution level +!define MULTIUSER_EXECUTIONLEVEL Highest +!define MULTIUSER_MUI +!define MULTIUSER_INSTALLMODE_COMMANDLINE +!define MULTIUSER_INSTALLMODE_INSTDIR "${INSTPATH}" ; without programs /appdata +; allow for next run of installer to automatically find install path and type +!define MULTIUSER_INSTALLMODE_INSTDIR_REGISTRY_KEY "${BASEREGKEY}" +!define MULTIUSER_INSTALLMODE_INSTDIR_REGISTRY_VALUENAME "InstallPath" +!define MULTIUSER_INSTALLMODE_DEFAULT_REGISTRY_KEY "${BASEREGKEY}" +!define MULTIUSER_INSTALLMODE_DEFAULT_REGISTRY_VALUENAME "InstallType" +!if ${BITS} == "64" + !define MULTIUSER_USE_PROGRAMFILES64 +!endif +!include MultiUser.nsh + +; Modern UI 2 +!include MUI2.nsh +!define MUI_ICON "${APPICON}" +!define MUI_ABORTWARNING +!define MUI_UNABORTWARNING + +;============================================================================== +; NSIS Variables +;============================================================================== + +Name "${APPNAME}" +!system 'mkdir "${DISTDIR}"' +OutFile "${DISTDIR}\${APPNAME}_win${BITS}_${VERSIONMAJOR}.${VERSIONMINOR}.${VERSIONPATCH}.exe" +Icon "${APPICON}" + +;============================================================================== +; Pages +;============================================================================== + +!insertmacro MUI_PAGE_WELCOME +!insertmacro MUI_PAGE_LICENSE "${APPLICENSE}" +!insertmacro MULTIUSER_PAGE_INSTALLMODE +!insertmacro MUI_PAGE_DIRECTORY + +; values for start menu page +!define MUI_STARTMENUPAGE_REGISTRY_ROOT "SHCTX" ; uses shell context +!define MUI_STARTMENUPAGE_REGISTRY_KEY "${BASEREGKEY}" +!define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "Start Menu Folder" +!insertmacro MUI_PAGE_STARTMENU Application $StartMenuFolder + +!insertmacro MUI_PAGE_INSTFILES +!insertmacro MUI_PAGE_FINISH + +; uninstaller pages +!insertmacro MUI_UNPAGE_CONFIRM +!insertmacro MUI_UNPAGE_INSTFILES + +;============================================================================== +; Languages +;============================================================================== + +!insertmacro MUI_LANGUAGE "English" ;first language is the default language +!insertmacro MUI_LANGUAGE "French" +!insertmacro MUI_LANGUAGE "German" +!insertmacro MUI_LANGUAGE "Greek" +!insertmacro MUI_LANGUAGE "Italian" +!insertmacro MUI_LANGUAGE "Korean" +!insertmacro MUI_LANGUAGE "Polish" +!insertmacro MUI_LANGUAGE "Russian" +!insertmacro MUI_LANGUAGE "Spanish" +!insertmacro MUI_LANGUAGE "Ukrainian" +!insertmacro MUI_LANGUAGE "Vietnamese" +!insertmacro MUI_LANGUAGE "Dutch" +!insertmacro MUI_LANGUAGE "Czech" +;!insertmacro MUI_LANGUAGE "Chinese" ; no NSIS builtin support +;!insertmacro MUI_LANGUAGE "Brazilian" ; no NSIS builtin support +;!insertmacro MUI_LANGUAGE "Armenian" ; requires UNICODE + +;============================================================================== +; Reserve Files +;============================================================================== + +; If you are using solid compression, files that are required before +; the actual installation should be stored first in the data block, +; because this will make your installer start faster. + +!insertmacro MUI_RESERVEFILE_LANGDLL +ReserveFile /nonfatal "${NSISDIR}\Plugins\*.dll" ;reserve if needed + +;============================================================================== +; Installer Sections +;============================================================================== + +Section "!Application" AppSec + SetOutPath "$INSTDIR" ; set from result of installer pages + + ; Files to install + File /r "${SOURCEPATH}\${APPNAME}-win${BITS}bit\*" + + ; Create Start Menu Items + !insertmacro MUI_STARTMENU_WRITE_BEGIN Application + CreateDirectory "$SMPROGRAMS\$StartMenuFolder" + CreateShortcut "$SMPROGRAMS\$StartMenuFolder\${APPNAME}.lnk" "$INSTDIR\${APPNAME}.exe" + CreateShortcut "$SMPROGRAMS\$StartMenuFolder\Uninstall.lnk" "$INSTDIR\Uninstall.exe" + !insertmacro MUI_STARTMENU_WRITE_END + + ; Store installation folder + WriteRegStr SHCTX "${BASEREGKEY}" "InstallPath" $INSTDIR + WriteRegStr SHCTX "${BASEREGKEY}" "InstallType" $MultiUser.InstallMode + + ; get installed size + Push $R0 + Push $R1 + Push $R2 + ${GetSize} "$INSTDIR" "/S=0K" $R0 $R1 $R2 ; look into locate + IntFmt $InstallSize "0x%08X" $R0 + Pop $R2 + Pop $R1 + Pop $R0 + + ; Uninstall Entry + WriteRegStr SHCTX "${UNINSTALLREG}" "DisplayName" "${APPNAME} ${VERSIONMAJOR}.${VERSIONMINOR}.${VERSIONPATCH}" + WriteRegStr SHCTX "${UNINSTALLREG}" "DisplayVersion" "${VERSIONMAJOR}.${VERSIONMINOR}.${VERSIONPATCH}" + WriteRegStr SHCTX "${UNINSTALLREG}" "DisplayIcon" "$INSTDIR\${APPNAME}.exe" + WriteRegDWORD SHCTX "${UNINSTALLREG}" "VersionMajor" ${VERSIONMAJOR} + WriteRegDWORD SHCTX "${UNINSTALLREG}" "VersionMinor" ${VERSIONMINOR} + WriteRegDWORD SHCTX "${UNINSTALLREG}" "VersionPatch" ${VERSIONPATCH} + WriteRegStr SHCTX "${UNINSTALLREG}" "Comments" "${APPNAME} installer" + WriteRegStr SHCTX "${UNINSTALLREG}" "InstallLocation" "$INSTDIR" + WriteRegStr SHCTX "${UNINSTALLREG}" "Publisher" "${COMPANYNAME}" + WriteRegStr SHCTX "${UNINSTALLREG}" "Contact" "${HELPURL}" + WriteRegStr SHCTX "${UNINSTALLREG}" "HelpLink" "${HELPURL}" + WriteRegStr SHCTX "${UNINSTALLREG}" "URLUpdateInfo" "${UPDATEURL}" + WriteRegStr SHCTX "${UNINSTALLREG}" "URLInfoAbout" "${ABOUTURL}" + WriteRegDWORD SHCTX "${UNINSTALLREG}" "NoModify" 1 + WriteRegDWORD SHCTX "${UNINSTALLREG}" "NoRepair" 1 + WriteRegDWORD SHCTX "${UNINSTALLREG}" "EstimatedSize" $InstallSize + WriteRegStr SHCTX "${UNINSTALLREG}" "UninstallString" "$\"$INSTDIR\uninstall.exe$\" /$MultiUser.InstallMode" + WriteRegStr SHCTX "${UNINSTALLREG}" "QuietUninstallString" "$\"$INSTDIR\uninstall.exe$\" /$MultiUser.InstallMode /S" + + ; Create uninstaller + WriteUninstaller "$INSTDIR\Uninstall.exe" +SectionEnd + +;============================================================================== +; Descriptions +;============================================================================== +; Add descriptions as needed + +;============================================================================== +; Uninstaller Sections +;============================================================================== + +Section "Uninstall" + ; Remove Start Menu Folder + !insertmacro MUI_STARTMENU_GETFOLDER Application $StartMenuFolder + RMDir /r "$SMPROGRAMS\$StartMenuFolder" + + ; Delete the Install Directory and vendor directory (if empty) + Push $R0 + RMDir /r "$INSTDIR" ;NSIS seems to recomend against this... look into options + ${GetParent} "$INSTDIR" $R0 + RMDir $R0 + Pop $R0 + + ; Remove registry keys and vendor keys (if empty) + DeleteRegKey SHCTX "${BASEREGKEY}" + DeleteRegKey /ifempty SHCTX "${VENDORREGKEY}" + DeleteRegKey SHCTX "${UNINSTALLREG}" +SectionEnd + +;============================================================================== +; Functions +;============================================================================== + +Function .onInit + !if ${BITS} == "64" + SetRegView 64 + !else + SetRegView 32 + !endif + !insertmacro MULTIUSER_INIT + !insertmacro MUI_LANGDLL_DISPLAY +FunctionEnd + +Function un.onInit + !if ${BITS} == "64" + SetRegView 64 + !else + SetRegView 32 + !endif + !insertmacro MULTIUSER_UNINIT + !insertmacro MUI_UNGETLANGUAGE +FunctionEnd \ No newline at end of file From a6b1e6e9abdd6abe6658309c3538cc4576c1d0a5 Mon Sep 17 00:00:00 2001 From: Virgil Dupras Date: Tue, 19 Sep 2017 13:14:11 -0400 Subject: [PATCH 2/7] Make tox work with non-venv interpreters Previously, as soon as an interpreter that wasn't the one having been used for `env` was used by tox, we would get errors because our C modules wouldn't be built for this interpreter. The makefile has been changed to make `make modules` interpreter-aware, thus fixing this problem. --- Makefile | 33 ++++++++++++++++----------------- tox.ini | 5 +++++ 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/Makefile b/Makefile index b8ccc50b..b5124872 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,5 @@ PYTHON ?= python3 +PYTHON_VERSION_MINOR := $(shell ${PYTHON} -c "import sys; print(sys.version_info.minor)") PYRCC5 ?= pyrcc5 REQ_MINOR_VERSION = 4 PREFIX ?= /usr/local @@ -10,11 +11,11 @@ PREFIX ?= /usr/local ifeq ($(shell uname -o), Msys) BIN = Scripts - SO = pyd + SO = *.pyd VENV_OPTIONS = else BIN = bin - SO = so + SO = cpython-3$(PYTHON_VERSION_MINOR)m*.so VENV_OPTIONS = --system-site-packages endif @@ -53,11 +54,9 @@ pyc: ${PYTHON} -m compileall ${packages} reqs : - @ret=`${PYTHON} -c "import sys; print(int(sys.version_info[:2] >= (3, ${REQ_MINOR_VERSION})))"`; \ - if [ $${ret} -ne 1 ]; then \ - echo "Python 3.${REQ_MINOR_VERSION}+ required. Aborting."; \ - exit 1; \ - fi +ifneq ($(shell test $(PYTHON_VERSION_MINOR) -gt $(REQ_MINOR_VERSION); echo $$?),0) + $(error "Python 3.${REQ_MINOR_VERSION}+ required. Aborting.") +endif ifndef NO_VENV @${PYTHON} -m venv -h > /dev/null || \ echo "Creation of our virtualenv failed. If you're on Ubuntu, you probably need python3-venv." @@ -91,19 +90,19 @@ i18n: $(mofiles) %.mo : %.po msgfmt -o $@ $< -core/pe/_block.*.$(SO) : core/pe/modules/block.c core/pe/modules/common.c | env - $(VENV_PYTHON) hscommon/build_ext.py $^ _block - mv _block.*.$(SO) core/pe +core/pe/_block.$(SO) : core/pe/modules/block.c core/pe/modules/common.c + $(PYTHON) hscommon/build_ext.py $^ _block + mv _block.$(SO) core/pe -core/pe/_cache.*.$(SO) : core/pe/modules/cache.c core/pe/modules/common.c | env - $(VENV_PYTHON) hscommon/build_ext.py $^ _cache - mv _cache.*.$(SO) core/pe +core/pe/_cache.$(SO) : core/pe/modules/cache.c core/pe/modules/common.c + $(PYTHON) hscommon/build_ext.py $^ _cache + mv _cache.$(SO) core/pe -qt/pe/_block_qt.*.$(SO) : qt/pe/modules/block.c | env - $(VENV_PYTHON) hscommon/build_ext.py $^ _block_qt - mv _block_qt.*.$(SO) qt/pe +qt/pe/_block_qt.$(SO) : qt/pe/modules/block.c + $(PYTHON) hscommon/build_ext.py $^ _block_qt + mv _block_qt.$(SO) qt/pe -modules : core/pe/_block.*.$(SO) core/pe/_cache.*.$(SO) qt/pe/_block_qt.*.$(SO) +modules : core/pe/_block.$(SO) core/pe/_cache.$(SO) qt/pe/_block_qt.$(SO) mergepot : $(VENV_PYTHON) build.py --mergepot diff --git a/tox.ini b/tox.ini index 5c7b7681..04be5170 100644 --- a/tox.ini +++ b/tox.ini @@ -4,7 +4,12 @@ skipsdist = True skip_missing_interpreters = True [testenv] +whitelist_externals = + make +setenv = + PYTHON="{envpython}" commands = + make modules flake8 py.test core hscommon deps = From 2f31dc7aab7c5d69a1d81b95e99e1fec34f961e7 Mon Sep 17 00:00:00 2001 From: Virgil Dupras Date: Tue, 19 Sep 2017 13:22:33 -0400 Subject: [PATCH 3/7] cache_shelve: wrap deletions in try..except in purge_outdated Hopefully solves #402 and #439. --- core/pe/cache_shelve.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/core/pe/cache_shelve.py b/core/pe/cache_shelve.py index cc4a39c0..fee51dad 100644 --- a/core/pe/cache_shelve.py +++ b/core/pe/cache_shelve.py @@ -126,6 +126,11 @@ class ShelveCache: continue todelete.append(path) for path in todelete: - del self[path] - + try: + del self[path] + except KeyError: + # I have no idea why a KeyError sometimes happen, but it does, as we can see in + # #402 and #439. I don't think it hurts to silently ignore the error, so that's + # what we do + pass From 7936339909ef2ab827f3659db2536348c16afc38 Mon Sep 17 00:00:00 2001 From: auanasgheps Date: Fri, 29 Sep 2017 00:49:00 +0100 Subject: [PATCH 4/7] Update columns.po (#445) Updated missing strings --- locale/it/LC_MESSAGES/columns.po | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/locale/it/LC_MESSAGES/columns.po b/locale/it/LC_MESSAGES/columns.po index c287121f..f1bc8279 100644 --- a/locale/it/LC_MESSAGES/columns.po +++ b/locale/it/LC_MESSAGES/columns.po @@ -45,7 +45,7 @@ msgstr "Modificato" #: core/me/prioritize.py:18 msgid "Duration" -msgstr "" +msgstr "Durata" #: core/me/prioritize.py:24 core/me/result_table.py:22 msgid "Bitrate" @@ -53,7 +53,7 @@ msgstr "Bitrate" #: core/me/prioritize.py:30 msgid "Samplerate" -msgstr "" +msgstr "Campionamento" #: core/me/result_table.py:20 msgid "Size (MB)" @@ -119,4 +119,4 @@ msgstr "Dimensione (KB)" #: core/pe/result_table.py:23 msgid "EXIF Timestamp" -msgstr "" +msgstr "Data EXIF " From 5d15cd4c97842726f475a220eee3f1d1d0f3f5f2 Mon Sep 17 00:00:00 2001 From: auanasgheps Date: Tue, 3 Oct 2017 16:50:41 +0100 Subject: [PATCH 5/7] Update columns.po (#446) Too much space on line #122 --- locale/it/LC_MESSAGES/columns.po | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/locale/it/LC_MESSAGES/columns.po b/locale/it/LC_MESSAGES/columns.po index f1bc8279..e59a2686 100644 --- a/locale/it/LC_MESSAGES/columns.po +++ b/locale/it/LC_MESSAGES/columns.po @@ -119,4 +119,4 @@ msgstr "Dimensione (KB)" #: core/pe/result_table.py:23 msgid "EXIF Timestamp" -msgstr "Data EXIF " +msgstr "Data EXIF" From 93a3978747efa845d821dfd6dd7247f8ab7eae8e Mon Sep 17 00:00:00 2001 From: auanasgheps Date: Tue, 3 Oct 2017 16:51:17 +0100 Subject: [PATCH 6/7] Update core.po (#447) All strings should now be translated in Italia. I've also changed Last-Translator string to my nickname, hope that is okay. --- locale/it/LC_MESSAGES/core.po | 51 ++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/locale/it/LC_MESSAGES/core.po b/locale/it/LC_MESSAGES/core.po index 25f9ba8b..5d90847a 100644 --- a/locale/it/LC_MESSAGES/core.po +++ b/locale/it/LC_MESSAGES/core.po @@ -3,7 +3,7 @@ msgid "" msgstr "" "Project-Id-Version: dupeGuru\n" "PO-Revision-Date: 2013-12-07 15:22+0000\n" -"Last-Translator: hsoft \n" +"Last-Translator: auanasgheps\n" "Language-Team: Italian (http://www.transifex.com/projects/p/dupeguru/language/it/)\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: utf-8\n" @@ -12,17 +12,18 @@ msgstr "" #: core/app.py:40 msgid "There are no marked duplicates. Nothing has been done." -msgstr "" +msgstr "Non ci sono duplicati marcati. Nessuna operazione è stata completata." #: core/app.py:41 msgid "There are no selected duplicates. Nothing has been done." -msgstr "" +msgstr "Non ci sono duplicati selezionati. Nessuna operazione è stata completata." #: core/app.py:42 msgid "" "You're about to open many files at once. Depending on what those files are " "opened with, doing so can create quite a mess. Continue?" -msgstr "" +msgstr "Stai per aprire molti file contemporaneamente. A seconda di quale programma +li aprirà, potrebbe crearsi un bel casino. Vuoi continuare?" #: core/app.py:65 msgid "Scanning for duplicates" @@ -66,7 +67,7 @@ msgstr "Tutti i file marcati sono stati spostati correttamente." #: core/app.py:306 msgid "All marked files were successfully sent to Trash." -msgstr "Tutti i file marcati sono stati inviati nel cestino." +msgstr "Tutti i file marcati sono stati spostati nel cestino." #: core/app.py:368 msgid "'{}' already is in the list." @@ -86,19 +87,19 @@ msgstr "" #: core/app.py:450 msgid "copy" -msgstr "" +msgstr "copiare" #: core/app.py:450 msgid "move" -msgstr "" +msgstr "spostare" #: core/app.py:451 msgid "Select a directory to {} marked files to" -msgstr "" +msgstr "Seleziona una cartella per {} in essa i file marcati" #: core/app.py:490 msgid "Select a destination for your exported CSV" -msgstr "" +msgstr "Seleziona una destinazione per il file CSV" #: core/app.py:518 msgid "You have no custom command set up. Set it up in your preferences." @@ -112,7 +113,7 @@ msgstr "Stai per rimuovere %d file dai risultati. Continuare?" #: core/app.py:719 msgid "{} duplicate groups were changed by the re-prioritization." -msgstr "" +msgstr "{} gruppi duplicati sono stati cambiati dalla nuova priorirità" #: core/app.py:762 msgid "Collecting files to scan" @@ -140,7 +141,7 @@ msgstr "Lettura dimensione di %d/%d file" #: core/gui/deletion_options.py:69 msgid "You are sending {} file(s) to the Trash." -msgstr "" +msgstr "Stai spostando {} file al Cestino." #: core/gui/ignore_list_dialog.py:24 msgid "Do you really want to remove all %d items from the ignore list?" @@ -150,7 +151,7 @@ msgstr "" #: core/prioritize.py:68 msgid "None" -msgstr "" +msgstr "Nessuno" #: core/prioritize.py:96 msgid "Ends with number" @@ -162,11 +163,11 @@ msgstr "Non termina con un numero" #: core/prioritize.py:98 msgid "Longest" -msgstr "" +msgstr "Più lungo" #: core/prioritize.py:99 msgid "Shortest" -msgstr "" +msgstr "Più corto" #: core/prioritize.py:132 msgid "Highest" @@ -214,47 +215,47 @@ msgstr "Verificate %d/%d somiglianze" #: core/pe/matchexif.py:18 msgid "Read EXIF of %d/%d pictures" -msgstr "" +msgstr "Leggi dati EXIF da %d/%d immagini" #: core/app.py:312 msgid "Could not load file: {}" -msgstr "" +msgstr "Impossibile caricare il file: {}" #: core/app.py:496 core/app.py:742 msgid "Couldn't write to file: {}" -msgstr "" +msgstr "Impossibile modificare il file: {}" #: core/me/scanner.py:19 core/se/scanner.py:15 msgid "Filename" -msgstr "" +msgstr "Nome del file" #: core/me/scanner.py:20 msgid "Filename - Fields" -msgstr "" +msgstr "Nome file - Campi" #: core/me/scanner.py:21 msgid "Filename - Fields (No Order)" -msgstr "" +msgstr "Nome file - Campi (Nessun Ordine)" #: core/me/scanner.py:22 msgid "Tags" -msgstr "" +msgstr "Tag" #: core/me/scanner.py:23 core/pe/scanner.py:20 core/se/scanner.py:16 msgid "Contents" -msgstr "" +msgstr "Contenuti" #: core/pe/scanner.py:21 msgid "EXIF Timestamp" -msgstr "" +msgstr "Timestamp EXIF" #: core/scanner.py:139 msgid "Almost done! Fiddling with results..." -msgstr "" +msgstr "Quasi finito! Sto organizzando i risultati..." #: core/se/scanner.py:17 msgid "Folders" -msgstr "" +msgstr "Cartelle" #~ msgid "Sending files to the recycle bin" #~ msgstr "Spostamento nel cestino" From 899a42f6a9718d996792bc2b9c99718b6dbf03d6 Mon Sep 17 00:00:00 2001 From: auanasgheps Date: Tue, 3 Oct 2017 16:52:26 +0100 Subject: [PATCH 7/7] Update ui.po (#448) More translations (not all yet). I've also revised some of them. Changed _Last-Translato_r string to my nickname, hope that is okay. --- locale/it/LC_MESSAGES/ui.po | 121 ++++++++++++++++++------------------ 1 file changed, 62 insertions(+), 59 deletions(-) diff --git a/locale/it/LC_MESSAGES/ui.po b/locale/it/LC_MESSAGES/ui.po index 89a2abf2..a6d9325c 100644 --- a/locale/it/LC_MESSAGES/ui.po +++ b/locale/it/LC_MESSAGES/ui.po @@ -3,7 +3,7 @@ msgid "" msgstr "" "Project-Id-Version: dupeGuru\n" "PO-Revision-Date: 2013-12-07 15:22+0000\n" -"Last-Translator: hsoft \n" +"Last-Translator: auanasgheps\n" "Language-Team: Italian (http://www.transifex.com/projects/p/dupeguru/language/it/)\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: utf-8\n" @@ -12,12 +12,12 @@ msgstr "" #: qt/app.py:81 msgid "Quit" -msgstr "" +msgstr "Esci" #: qt/app.py:83 qt/ignore_list_dialog.py:32 #: cocoa/en.lproj/Localizable.strings:0 msgid "Ignore List" -msgstr "" +msgstr "Lista elementi ignorati" #: qt/app.py:85 cocoa/en.lproj/Localizable.strings:0 msgid "dupeGuru Help" @@ -29,55 +29,60 @@ msgstr "Informazioni su dupeGuru" #: qt/app.py:87 msgid "Open Debug Log" -msgstr "" +msgstr "Apri registro eventi" #: qt/app.py:251 msgid "{} file (*.{})" -msgstr "" +msgstr "{} file (*.{})" #: qt/deletion_options.py:30 cocoa/en.lproj/Localizable.strings:0 msgid "Deletion Options" -msgstr "" +msgstr "Opzioni di eliminazione" #: qt/deletion_options.py:35 cocoa/en.lproj/Localizable.strings:0 msgid "Link deleted files" -msgstr "" +msgstr "Collega file eliminati" #: qt/deletion_options.py:37 cocoa/en.lproj/Localizable.strings:0 msgid "" "After having deleted a duplicate, place a link targeting the reference file " "to replace the deleted file." msgstr "" +"Dopo aver selezionato un duplicato, per sostituire il file eliminato posiziona un collegamento " +"che ha come destinazione i file di referenza." #: qt/deletion_options.py:44 msgid "Hardlink" -msgstr "" +msgstr "Hardlink" #: qt/deletion_options.py:44 msgid "Symlink" -msgstr "" +msgstr "Symlink" #: qt/deletion_options.py:48 msgid " (unsupported)" -msgstr "" +msgstr " (non supportato)" #: qt/deletion_options.py:49 cocoa/en.lproj/Localizable.strings:0 msgid "Directly delete files" -msgstr "" +msgstr "Elimina file direttamente" #: qt/deletion_options.py:51 cocoa/en.lproj/Localizable.strings:0 msgid "" "Instead of sending files to trash, delete them directly. This option is " "usually used as a workaround when the normal deletion method doesn't work." msgstr "" +"Invece di spostare file nel cestino, eliminali direttamente. Questa opzione " +"di solito è usata come alternativa al sistema di eliminazione standard quando " +"non funziona." #: qt/deletion_options.py:59 cocoa/en.lproj/Localizable.strings:0 msgid "Proceed" -msgstr "" +msgstr "Procedi" #: qt/deletion_options.py:60 cocoa/en.lproj/Localizable.strings:0 msgid "Cancel" -msgstr "Cancella" +msgstr "Annulla" #: qt/details_table.py:16 cocoa/en.lproj/Localizable.strings:0 msgid "Attribute" @@ -94,7 +99,7 @@ msgstr "Riferimento" #: qt/directories_dialog.py:64 cocoa/en.lproj/Localizable.strings:0 msgid "Load Results..." -msgstr "Carica i risultati..." +msgstr "Caricamento risultati..." #: qt/directories_dialog.py:65 cocoa/en.lproj/Localizable.strings:0 msgid "Results Window" @@ -102,7 +107,7 @@ msgstr "Finestra dei risultati" #: qt/directories_dialog.py:66 msgid "Add Folder..." -msgstr "" +msgstr "Aggiungi Cartella..." #: qt/directories_dialog.py:74 qt/result_window.py:100 #: cocoa/en.lproj/Localizable.strings:0 @@ -111,7 +116,7 @@ msgstr "File" #: qt/directories_dialog.py:76 qt/result_window.py:108 msgid "View" -msgstr "" +msgstr "Visualizzazione" #: qt/directories_dialog.py:78 qt/result_window.py:110 #: cocoa/en.lproj/Localizable.strings:0 @@ -136,7 +141,7 @@ msgstr "Scansiona" #: qt/directories_dialog.py:230 msgid "Unsaved results" -msgstr "" +msgstr "Risultati non salvati" #: qt/directories_dialog.py:231 cocoa/en.lproj/Localizable.strings:0 msgid "You have unsaved results, do you really want to quit?" @@ -144,9 +149,7 @@ msgstr "Hai dei risultati non salvati. Vuoi veramente chiudere?" #: qt/directories_dialog.py:239 cocoa/en.lproj/Localizable.strings:0 msgid "Select a folder to add to the scanning list" -msgstr "" -"Seleziona una cartella da aggiungere alla lista delle cartelle da " -"scansionare" +msgstr "Seleziona una cartella da aggiungere alla lista delle cartelle da scansionare" #: qt/directories_dialog.py:266 cocoa/en.lproj/Localizable.strings:0 msgid "Select a results file to load" @@ -154,15 +157,15 @@ msgstr "Seleziona un risultato (file) da caricare" #: qt/directories_dialog.py:267 msgid "All Files (*.*)" -msgstr "" +msgstr "Tutti i file (*.*)" #: qt/directories_dialog.py:267 qt/result_window.py:311 msgid "dupeGuru Results (*.dupeguru)" -msgstr "" +msgstr "Risultati dupeGuru (*.dupeguru)" #: qt/directories_dialog.py:278 msgid "Start a new scan" -msgstr "" +msgstr "Inizia una nuova scansione" #: qt/directories_dialog.py:279 cocoa/en.lproj/Localizable.strings:0 msgid "You have unsaved results, do you really want to continue?" @@ -186,11 +189,11 @@ msgstr "Normale" #: qt/ignore_list_dialog.py:45 cocoa/en.lproj/Localizable.strings:0 msgid "Remove Selected" -msgstr "" +msgstr "Rimuovi Selezionati" #: qt/ignore_list_dialog.py:46 cocoa/en.lproj/Localizable.strings:0 msgid "Clear" -msgstr "" +msgstr "Deseleziona" #: qt/ignore_list_dialog.py:47 qt/problem_dialog.py:61 #: cocoa/en.lproj/Localizable.strings:0 @@ -199,19 +202,19 @@ msgstr "Chiudi" #: qt/directories_dialog.py:128 cocoa/en.lproj/Localizable.strings:0 msgid "Scan Type:" -msgstr "" +msgstr "Tipo di scansione:" #: qt/preferences_dialog.py:43 msgid "Filter Hardness:" -msgstr "" +msgstr "Durezza del filtro:" #: qt/preferences_dialog.py:69 msgid "More Results" -msgstr "" +msgstr "Più risultati" #: qt/preferences_dialog.py:74 msgid "Fewer Results" -msgstr "" +msgstr "Meno risultati" #: qt/preferences_dialog.py:81 msgid "Font size:" @@ -219,7 +222,7 @@ msgstr "Dimensione carattere:" #: qt/preferences_dialog.py:85 msgid "Language:" -msgstr "" +msgstr "Lingua:" #: qt/preferences_dialog.py:91 cocoa/en.lproj/Localizable.strings:0 msgid "Copy and Move:" @@ -244,7 +247,7 @@ msgstr "" #: qt/preferences_dialog.py:174 msgid "dupeGuru has to restart for language changes to take effect." -msgstr "" +msgstr "Per cambiare lingua, dupeGuru deve riavviarsi." #: qt/prioritize_dialog.py:75 cocoa/en.lproj/Localizable.strings:0 msgid "Re-Prioritize duplicates" @@ -299,15 +302,15 @@ msgstr "Visualizza le differenze" #: qt/result_window.py:60 msgid "Send Marked to Recycle Bin..." -msgstr "" +msgstr "Sposta elementi marcati nel Cestino..." #: qt/result_window.py:61 cocoa/en.lproj/Localizable.strings:0 msgid "Move Marked to..." -msgstr "Sposta gli elementi marcati nel..." +msgstr "Sposta elementi marcati nel..." #: qt/result_window.py:62 cocoa/en.lproj/Localizable.strings:0 msgid "Copy Marked to..." -msgstr "Copia gli elementi evidenziati nel..." +msgstr "Copia elementi evidenziati nel..." #: qt/result_window.py:63 cocoa/en.lproj/Localizable.strings:0 msgid "Remove Marked from Results" @@ -335,7 +338,7 @@ msgstr "Apri gli elementi selezionati con l'applicazione predefinita" #: qt/result_window.py:80 msgid "Open Containing Folder of Selected" -msgstr "" +msgstr "Apri cartella degli elementi selezionati" #: qt/result_window.py:82 cocoa/en.lproj/Localizable.strings:0 msgid "Rename Selected" @@ -359,11 +362,11 @@ msgstr "Marca i selezionati" #: qt/result_window.py:87 msgid "Export To HTML" -msgstr "" +msgstr "Esporta in HTML" #: qt/result_window.py:88 msgid "Export To CSV" -msgstr "" +msgstr "Esporta in CSV" #: qt/result_window.py:89 cocoa/en.lproj/Localizable.strings:0 msgid "Save Results..." @@ -375,7 +378,7 @@ msgstr "Invoca comando personalizzato" #: qt/result_window.py:102 msgid "Mark" -msgstr "" +msgstr "Marca" #: qt/result_window.py:106 cocoa/en.lproj/Localizable.strings:0 msgid "Columns" @@ -387,7 +390,7 @@ msgstr "Ripristina impostazioni predefinite" #: qt/result_window.py:185 msgid "{} Results" -msgstr "" +msgstr "{} Resultati" #: qt/result_window.py:193 cocoa/en.lproj/Localizable.strings:0 msgid "Dupes Only" @@ -395,7 +398,7 @@ msgstr "Solo duplicati" #: qt/result_window.py:194 msgid "Delta Values" -msgstr "" +msgstr "Valori Delta" #: qt/result_window.py:310 cocoa/en.lproj/Localizable.strings:0 msgid "Select a file to save your results to" @@ -484,7 +487,7 @@ msgstr "" #: qt/se/preferences_dialog.py:41 msgid "Ignore files smaller than" -msgstr "" +msgstr "Ignora file più piccoli di" #: qt/se/preferences_dialog.py:52 cocoa/en.lproj/Localizable.strings:0 msgid "KB" @@ -492,7 +495,7 @@ msgstr "KB" #: cocoa/en.lproj/Localizable.strings:0 msgid "%@ Results" -msgstr "" +msgstr "%@ Resultati" #: cocoa/en.lproj/Localizable.strings:0 msgid "Action" @@ -557,7 +560,7 @@ msgstr "Cartelle" #: cocoa/en.lproj/Localizable.strings:0 msgid "dupeGuru" -msgstr "" +msgstr "dupeGuru" #: cocoa/en.lproj/Localizable.strings:0 msgid "dupeGuru Preferences" @@ -573,11 +576,11 @@ msgstr "Sito Web di dupeGuru" #: cocoa/en.lproj/Localizable.strings:0 msgid "Edit" -msgstr "Edita" +msgstr "Modifica" #: cocoa/en.lproj/Localizable.strings:0 msgid "Export Results to CSV" -msgstr "" +msgstr "Esporta risultati in CSV" #: cocoa/en.lproj/Localizable.strings:0 msgid "Export Results to XHTML" @@ -597,7 +600,7 @@ msgstr "Durezza del filtro:" #: cocoa/en.lproj/Localizable.strings:0 msgid "Filter Results..." -msgstr "" +msgstr "Filtra Risultati..." #: cocoa/en.lproj/Localizable.strings:0 msgid "Folder Selection Window" @@ -605,7 +608,7 @@ msgstr "Finestra di selezione della cartella" #: cocoa/en.lproj/Localizable.strings:0 msgid "Font Size:" -msgstr "" +msgstr "Dimensione Carattere:" #: cocoa/en.lproj/Localizable.strings:0 msgid "Hide dupeGuru" @@ -654,7 +657,7 @@ msgstr "Preferenze..." #: cocoa/en.lproj/Localizable.strings:0 msgid "Quick Look" -msgstr "" +msgstr "Sguardo rapido" #: cocoa/en.lproj/Localizable.strings:0 msgid "Quit dupeGuru" @@ -666,7 +669,7 @@ msgstr "Ripristina le impostazioni predefinite" #: cocoa/en.lproj/Localizable.strings:0 msgid "Reset To Defaults" -msgstr "" +msgstr "Imposta prefefinite" #: cocoa/en.lproj/Localizable.strings:0 msgid "Reveal" @@ -678,7 +681,7 @@ msgstr "Mostra gli elementi selezionati nel Finder" #: cocoa/en.lproj/Localizable.strings:0 msgid "Select All" -msgstr "" +msgstr "Seleziona tutto" #: cocoa/en.lproj/Localizable.strings:0 msgid "Send Marked to Trash..." @@ -686,7 +689,7 @@ msgstr "Sposta gli elementi marcati nel cestino..." #: cocoa/en.lproj/Localizable.strings:0 msgid "Services" -msgstr "" +msgstr "Servizi" #: cocoa/en.lproj/Localizable.strings:0 msgid "Show All" @@ -710,23 +713,23 @@ msgstr "Zoom" #: qt/directories_dialog.py:116 cocoa/en.lproj/Localizable.strings:0 msgid "Application Mode:" -msgstr "" +msgstr "Modalità applicazione:" #: qt/directories_dialog.py:121 cocoa/en.lproj/Localizable.strings:0 msgid "Music" -msgstr "" +msgstr "Musica" #: qt/directories_dialog.py:121 cocoa/en.lproj/Localizable.strings:0 msgid "Picture" -msgstr "" +msgstr "Foto" #: qt/directories_dialog.py:121 cocoa/en.lproj/Localizable.strings:0 msgid "Standard" -msgstr "" +msgstr "Standard" #: qt/directories_dialog.py:135 msgid "More Options" -msgstr "" +msgstr "Più Opzioni" #~ msgid "Removing dead tracks from your iTunes Library" #~ msgstr "Rimozione delle tracce insistenti dalla libreria di iTunes" @@ -765,10 +768,10 @@ msgstr "" #~ msgstr "Non trovo l'applicazione iPhoto." #~ msgid "Preferences" -#~ msgstr "" +#~ msgstr "Impostazioni" #~ msgid "Check for Update" -#~ msgstr "" +#~ msgstr "Controlla aggiornamenti" #~ msgid "Filename" #~ msgstr "Nome del file" @@ -786,7 +789,7 @@ msgstr "" #~ msgstr "Contenuti" #~ msgid "Audio Contents" -#~ msgstr "" +#~ msgstr "Contenuti Audio" #~ msgid "EXIF Timestamp" #~ msgstr "Data e ora EXIF"