2009-06-01 09:55:11 +00:00
|
|
|
import logging
|
|
|
|
|
2012-01-13 19:43:43 +00:00
|
|
|
from objp.util import pyref, dontwrap
|
2016-06-06 15:20:45 +00:00
|
|
|
from hscommon.path import Path, pathify
|
2013-10-12 17:54:13 +00:00
|
|
|
from cocoa import install_exception_hook, install_cocoa_logger, patch_threaded_job_performer
|
2013-11-30 22:54:40 +00:00
|
|
|
from cocoa.inter import PyBaseApp, BaseAppView
|
2009-06-01 09:55:11 +00:00
|
|
|
|
2016-06-06 15:20:45 +00:00
|
|
|
import core.pe.photo
|
|
|
|
from core.app import DupeGuru as DupeGuruBase, AppMode
|
|
|
|
from .directories import Directories, Bundle
|
|
|
|
from .photo import Photo
|
|
|
|
|
|
|
|
class DupeGuru(DupeGuruBase):
|
|
|
|
def __init__(self, view):
|
|
|
|
DupeGuruBase.__init__(self, view)
|
|
|
|
self.directories = Directories()
|
|
|
|
|
|
|
|
def selected_dupe_path(self):
|
|
|
|
if not self.selected_dupes:
|
|
|
|
return None
|
|
|
|
return self.selected_dupes[0].path
|
|
|
|
|
|
|
|
def selected_dupe_ref_path(self):
|
|
|
|
if not self.selected_dupes:
|
|
|
|
return None
|
|
|
|
ref = self.results.get_group_of_duplicate(self.selected_dupes[0]).ref
|
|
|
|
if ref is self.selected_dupes[0]: # we don't want the same pic to be displayed on both sides
|
|
|
|
return None
|
|
|
|
return ref.path
|
|
|
|
|
|
|
|
def _get_fileclasses(self):
|
|
|
|
result = DupeGuruBase._get_fileclasses(self)
|
|
|
|
if self.app_mode == AppMode.Standard:
|
|
|
|
result = [Bundle] + result
|
|
|
|
return result
|
|
|
|
|
2013-11-30 22:54:40 +00:00
|
|
|
class DupeGuruView(BaseAppView):
|
2012-03-09 16:34:08 +00:00
|
|
|
def askYesNoWithPrompt_(self, prompt: str) -> bool: pass
|
2016-06-06 01:18:48 +00:00
|
|
|
def createResultsWindow(self): pass
|
2016-06-05 01:18:14 +00:00
|
|
|
def showResultsWindow(self): pass
|
2012-03-09 18:47:28 +00:00
|
|
|
def showProblemDialog(self): pass
|
2012-03-10 19:32:56 +00:00
|
|
|
def selectDestFolderWithPrompt_(self, prompt: str) -> str: pass
|
2012-07-31 20:46:51 +00:00
|
|
|
def selectDestFileWithPrompt_extension_(self, prompt: str, extension: str) -> str: pass
|
2012-01-13 19:43:43 +00:00
|
|
|
|
2016-06-06 15:20:45 +00:00
|
|
|
class PyDupeGuru(PyBaseApp):
|
2012-01-15 16:00:34 +00:00
|
|
|
@dontwrap
|
2016-06-06 15:20:45 +00:00
|
|
|
def __init__(self):
|
|
|
|
core.pe.photo.PLAT_SPECIFIC_PHOTO_CLASS = Photo
|
2011-09-20 22:40:27 +00:00
|
|
|
logging.basicConfig(level=logging.WARNING, format='%(levelname)s %(message)s')
|
2014-03-30 14:07:01 +00:00
|
|
|
install_exception_hook('https://github.com/hsoft/dupeguru/issues')
|
2012-08-01 15:52:59 +00:00
|
|
|
install_cocoa_logger()
|
2013-08-03 20:27:36 +00:00
|
|
|
patch_threaded_job_performer()
|
2016-06-06 15:20:45 +00:00
|
|
|
self.model = DupeGuru(self)
|
2011-09-20 22:40:27 +00:00
|
|
|
|
2012-01-13 19:43:43 +00:00
|
|
|
#---Sub-proxies
|
|
|
|
def detailsPanel(self) -> pyref:
|
|
|
|
return self.model.details_panel
|
|
|
|
|
|
|
|
def directoryTree(self) -> pyref:
|
|
|
|
return self.model.directory_tree
|
|
|
|
|
2012-01-13 20:25:34 +00:00
|
|
|
def problemDialog(self) -> pyref:
|
|
|
|
return self.model.problem_dialog
|
|
|
|
|
2012-01-13 19:43:43 +00:00
|
|
|
def statsLabel(self) -> pyref:
|
|
|
|
return self.model.stats_label
|
|
|
|
|
|
|
|
def resultTable(self) -> pyref:
|
|
|
|
return self.model.result_table
|
2011-11-27 17:47:00 +00:00
|
|
|
|
2012-03-14 16:47:21 +00:00
|
|
|
def ignoreListDialog(self) -> pyref:
|
|
|
|
return self.model.ignore_list_dialog
|
|
|
|
|
2013-08-03 20:27:36 +00:00
|
|
|
def progressWindow(self) -> pyref:
|
|
|
|
return self.model.progress_window
|
|
|
|
|
2012-05-30 16:10:56 +00:00
|
|
|
def deletionOptions(self) -> pyref:
|
|
|
|
return self.model.deletion_options
|
|
|
|
|
2011-09-20 22:40:27 +00:00
|
|
|
#---Directories
|
2012-07-31 19:33:44 +00:00
|
|
|
def addDirectory_(self, directory: str):
|
|
|
|
self.model.add_directory(directory)
|
2011-09-20 22:40:27 +00:00
|
|
|
|
|
|
|
#---Results
|
|
|
|
def doScan(self):
|
2012-01-13 19:43:43 +00:00
|
|
|
self.model.start_scanning()
|
2011-09-20 22:40:27 +00:00
|
|
|
|
2012-07-31 20:46:51 +00:00
|
|
|
def exportToXHTML(self):
|
|
|
|
self.model.export_to_xhtml()
|
|
|
|
|
|
|
|
def exportToCSV(self):
|
|
|
|
self.model.export_to_csv()
|
2011-09-20 22:40:27 +00:00
|
|
|
|
|
|
|
def loadSession(self):
|
2012-01-13 19:43:43 +00:00
|
|
|
self.model.load()
|
2011-09-20 22:40:27 +00:00
|
|
|
|
2012-01-13 19:43:43 +00:00
|
|
|
def loadResultsFrom_(self, filename: str):
|
|
|
|
self.model.load_from(filename)
|
2011-09-20 22:40:27 +00:00
|
|
|
|
|
|
|
def markAll(self):
|
2012-01-13 19:43:43 +00:00
|
|
|
self.model.mark_all()
|
2011-09-20 22:40:27 +00:00
|
|
|
|
|
|
|
def markNone(self):
|
2012-01-13 19:43:43 +00:00
|
|
|
self.model.mark_none()
|
2011-09-20 22:40:27 +00:00
|
|
|
|
|
|
|
def markInvert(self):
|
2012-01-13 19:43:43 +00:00
|
|
|
self.model.mark_invert()
|
2011-09-20 22:40:27 +00:00
|
|
|
|
|
|
|
def purgeIgnoreList(self):
|
2012-01-13 19:43:43 +00:00
|
|
|
self.model.purge_ignore_list()
|
2011-09-20 22:40:27 +00:00
|
|
|
|
|
|
|
def toggleSelectedMark(self):
|
2012-01-13 19:43:43 +00:00
|
|
|
self.model.toggle_selected_mark_state()
|
2011-09-20 22:40:27 +00:00
|
|
|
|
|
|
|
def saveSession(self):
|
2012-01-13 19:43:43 +00:00
|
|
|
self.model.save()
|
2011-09-20 22:40:27 +00:00
|
|
|
|
2012-01-13 19:43:43 +00:00
|
|
|
def saveResultsAs_(self, filename: str):
|
|
|
|
self.model.save_as(filename)
|
2011-09-20 22:40:27 +00:00
|
|
|
|
|
|
|
#---Actions
|
|
|
|
def addSelectedToIgnoreList(self):
|
2012-01-13 19:43:43 +00:00
|
|
|
self.model.add_selected_to_ignore_list()
|
2011-09-20 22:40:27 +00:00
|
|
|
|
|
|
|
def deleteMarked(self):
|
2012-01-13 19:43:43 +00:00
|
|
|
self.model.delete_marked()
|
2011-09-20 22:40:27 +00:00
|
|
|
|
2012-01-13 19:43:43 +00:00
|
|
|
def applyFilter_(self, filter: str):
|
|
|
|
self.model.apply_filter(filter)
|
2011-09-20 22:40:27 +00:00
|
|
|
|
|
|
|
def makeSelectedReference(self):
|
2012-01-13 19:43:43 +00:00
|
|
|
self.model.make_selected_reference()
|
2011-09-20 22:40:27 +00:00
|
|
|
|
2012-03-10 19:32:56 +00:00
|
|
|
def copyMarked(self):
|
|
|
|
self.model.copy_or_move_marked(copy=True)
|
|
|
|
|
|
|
|
def moveMarked(self):
|
|
|
|
self.model.copy_or_move_marked(copy=False)
|
2011-09-20 22:40:27 +00:00
|
|
|
|
|
|
|
def openSelected(self):
|
2012-01-13 19:43:43 +00:00
|
|
|
self.model.open_selected()
|
2011-09-20 22:40:27 +00:00
|
|
|
|
|
|
|
def removeMarked(self):
|
2012-01-13 19:43:43 +00:00
|
|
|
self.model.remove_marked()
|
2011-09-20 22:40:27 +00:00
|
|
|
|
2012-03-10 15:58:08 +00:00
|
|
|
def removeSelected(self):
|
|
|
|
self.model.remove_selected()
|
|
|
|
|
2011-09-20 22:40:27 +00:00
|
|
|
def revealSelected(self):
|
2012-01-13 19:43:43 +00:00
|
|
|
self.model.reveal_selected()
|
2012-03-10 15:58:08 +00:00
|
|
|
|
|
|
|
def invokeCustomCommand(self):
|
|
|
|
self.model.invoke_custom_command()
|
2011-09-20 22:40:27 +00:00
|
|
|
|
2012-03-14 16:47:21 +00:00
|
|
|
def showIgnoreList(self):
|
|
|
|
self.model.ignore_list_dialog.show()
|
|
|
|
|
2016-06-06 01:18:48 +00:00
|
|
|
def clearPictureCache(self):
|
|
|
|
self.model.clear_picture_cache()
|
|
|
|
|
2011-09-20 22:40:27 +00:00
|
|
|
#---Information
|
2016-06-03 01:31:12 +00:00
|
|
|
def getScanOptions(self) -> list:
|
|
|
|
return [o.label for o in self.model.SCANNER_CLASS.get_scan_options()]
|
|
|
|
|
2012-01-13 19:43:43 +00:00
|
|
|
def resultsAreModified(self) -> bool:
|
|
|
|
return self.model.results.is_modified
|
2011-09-20 22:40:27 +00:00
|
|
|
|
2016-06-06 01:18:48 +00:00
|
|
|
def getSelectedDupePath(self) -> str:
|
|
|
|
return str(self.model.selected_dupe_path())
|
|
|
|
|
|
|
|
def getSelectedDupeRefPath(self) -> str:
|
|
|
|
return str(self.model.selected_dupe_ref_path())
|
|
|
|
|
2011-09-20 22:40:27 +00:00
|
|
|
#---Properties
|
2016-06-06 01:18:48 +00:00
|
|
|
def getAppMode(self) -> int:
|
|
|
|
return self.model.app_mode
|
|
|
|
|
|
|
|
def setAppMode_(self, app_mode: int):
|
|
|
|
self.model.app_mode = app_mode
|
|
|
|
|
|
|
|
def setScanType_(self, scan_type_index: int):
|
|
|
|
scan_options = self.model.SCANNER_CLASS.get_scan_options()
|
|
|
|
try:
|
|
|
|
so = scan_options[scan_type_index]
|
|
|
|
self.model.options['scan_type'] = so.scan_type
|
|
|
|
except IndexError:
|
|
|
|
pass
|
|
|
|
|
|
|
|
def setMinMatchPercentage_(self, percentage: int):
|
|
|
|
self.model.options['min_match_percentage'] = int(percentage)
|
|
|
|
|
|
|
|
def setWordWeighting_(self, words_are_weighted: bool):
|
|
|
|
self.model.options['word_weighting'] = words_are_weighted
|
|
|
|
|
|
|
|
def setMatchSimilarWords_(self, match_similar_words: bool):
|
|
|
|
self.model.options['match_similar_words'] = match_similar_words
|
|
|
|
|
|
|
|
def setSizeThreshold_(self, size_threshold: int):
|
|
|
|
self.model.options['size_threshold'] = size_threshold
|
|
|
|
|
|
|
|
def enable_scanForTag_(self, enable: bool, scan_tag: str):
|
|
|
|
if 'scanned_tags' not in self.model.options:
|
|
|
|
self.model.options['scanned_tags'] = set()
|
|
|
|
if enable:
|
|
|
|
self.model.options['scanned_tags'].add(scan_tag)
|
|
|
|
else:
|
|
|
|
self.model.options['scanned_tags'].discard(scan_tag)
|
|
|
|
|
|
|
|
def setMatchScaled_(self, match_scaled: bool):
|
|
|
|
self.model.options['match_scaled'] = match_scaled
|
|
|
|
|
2012-01-13 19:43:43 +00:00
|
|
|
def setMixFileKind_(self, mix_file_kind: bool):
|
2016-06-02 01:56:18 +00:00
|
|
|
self.model.options['mix_file_kind'] = mix_file_kind
|
2011-09-20 22:40:27 +00:00
|
|
|
|
2012-01-13 19:43:43 +00:00
|
|
|
def setEscapeFilterRegexp_(self, escape_filter_regexp: bool):
|
|
|
|
self.model.options['escape_filter_regexp'] = escape_filter_regexp
|
2011-09-20 22:40:27 +00:00
|
|
|
|
2012-01-13 19:43:43 +00:00
|
|
|
def setRemoveEmptyFolders_(self, remove_empty_folders: bool):
|
|
|
|
self.model.options['clean_empty_dirs'] = remove_empty_folders
|
2011-09-20 22:40:27 +00:00
|
|
|
|
2012-01-13 19:43:43 +00:00
|
|
|
def setIgnoreHardlinkMatches_(self, ignore_hardlink_matches: bool):
|
|
|
|
self.model.options['ignore_hardlink_matches'] = ignore_hardlink_matches
|
2011-09-20 22:40:27 +00:00
|
|
|
|
2012-03-10 19:32:56 +00:00
|
|
|
def setCopyMoveDestType_(self, copymove_dest_type: int):
|
|
|
|
self.model.options['copymove_dest_type'] = copymove_dest_type
|
|
|
|
|
2011-09-20 22:40:27 +00:00
|
|
|
#--- model --> view
|
2012-03-09 16:34:08 +00:00
|
|
|
@dontwrap
|
|
|
|
def ask_yes_no(self, prompt):
|
|
|
|
return self.callback.askYesNoWithPrompt_(prompt)
|
|
|
|
|
2016-06-06 01:18:48 +00:00
|
|
|
@dontwrap
|
|
|
|
def create_results_window(self):
|
|
|
|
self.callback.createResultsWindow()
|
|
|
|
|
2012-03-09 18:47:28 +00:00
|
|
|
@dontwrap
|
|
|
|
def show_results_window(self):
|
2016-06-05 01:18:14 +00:00
|
|
|
self.callback.showResultsWindow()
|
2012-03-09 18:47:28 +00:00
|
|
|
|
|
|
|
@dontwrap
|
|
|
|
def show_problem_dialog(self):
|
|
|
|
self.callback.showProblemDialog()
|
|
|
|
|
2012-03-10 19:32:56 +00:00
|
|
|
@dontwrap
|
|
|
|
def select_dest_folder(self, prompt):
|
|
|
|
return self.callback.selectDestFolderWithPrompt_(prompt)
|
|
|
|
|
2012-07-31 20:46:51 +00:00
|
|
|
@dontwrap
|
|
|
|
def select_dest_file(self, prompt, extension):
|
|
|
|
return self.callback.selectDestFileWithPrompt_extension_(prompt, extension)
|
|
|
|
|