From cb8bb5a70e7c12810a32c6fdde648ac4da58daa4 Mon Sep 17 00:00:00 2001 From: Virgil Dupras Date: Fri, 6 Dec 2013 15:48:01 -0500 Subject: [PATCH] Disable symlink/hardlink option when not relevant (Qt) When the "Replace with links" option is not enabled, the choice of symlink or hardlink is irrelevant and causes confusion. Implemented core mechanism for controlling the enabled state of that option. Also implemented the Qt interface for it. Cocoa-part is still to be done. I used this opportunity to greatly enhance documentation of this part of the code. I'm beginning to like documenting... Ref #247. --- core/gui/deletion_options.py | 75 +++++++++++++++++-- help/en/developer/core/gui.rst | 5 -- .../developer/core/gui/deletion_options.rst | 5 ++ help/en/developer/core/gui/index.rst | 10 +++ help/en/developer/core/index.rst | 2 +- qt/base/deletion_options.py | 11 ++- 6 files changed, 95 insertions(+), 13 deletions(-) delete mode 100644 help/en/developer/core/gui.rst create mode 100644 help/en/developer/core/gui/deletion_options.rst create mode 100644 help/en/developer/core/gui/index.rst diff --git a/core/gui/deletion_options.py b/core/gui/deletion_options.py index 217089c2..54dcdd86 100644 --- a/core/gui/deletion_options.py +++ b/core/gui/deletion_options.py @@ -10,14 +10,60 @@ import os from hscommon.gui.base import GUIObject from hscommon.trans import tr +class DeletionOptionsView: + """Expected interface for :class:`DeletionOptions`'s view. + + *Not actually used in the code. For documentation purposes only.* + + Our view presents the user with an appropriate way (probably a mix of checkboxes and radio + buttons) to set the different flags in :class:`DeletionOptions`. Note that + :attr:`DeletionOptions.use_hardlinks` is only relevant if :attr:`DeletionOptions.link_deleted` + is true. This is why we toggle the "enabled" state of that flag. + + We expect the view to set :attr:`DeletionOptions.link_deleted` immediately as the user changes + its value because it will toggle :meth:`set_hardlink_option_enabled` + + Other than the flags, there's also a prompt message which has a dynamic content, defined by + :meth:`update_msg`. + """ + def update_msg(self, msg: str): + """Update the dialog's prompt with ``str``. + """ + + def show(self): + """Show the dialog in a modal fashion. + + Returns whether the dialog was "accepted" (the user pressed OK). + """ + + def set_hardlink_option_enabled(self, is_enabled: bool): + """Enable or disable the widget controlling :attr:`DeletionOptions.use_hardlinks`. + """ + class DeletionOptions(GUIObject): - #--- View interface - # update_msg(msg: str) - # show() - # + """Present the user with deletion options before proceeding. + + When the user activates "Send to trash", we present him with a couple of options that changes + the behavior of that deletion operation. + """ + def __init__(self): + GUIObject.__init__(self) + #: Whether symlinks or hardlinks are used when doing :attr:`link_deleted`. + #: *bool*. *get/set* + self.use_hardlinks = False + #: Delete dupes directly and don't send to trash. + #: *bool*. *get/set* + self.direct = False def show(self, mark_count): - self.link_deleted = False + """Prompt the user with a modal dialog offering our deletion options. + + :param int mark_count: Number of dupes marked for deletion. + :rtype: bool + :returns: Whether the user accepted the dialog (we cancel deletion if false). + """ + self._link_deleted = False + self.view.set_hardlink_option_enabled(False) self.use_hardlinks = False self.direct = False msg = tr("You are sending {} file(s) to the Trash.").format(mark_count) @@ -25,6 +71,8 @@ class DeletionOptions(GUIObject): return self.view.show() def supports_links(self): + """Returns whether our platform supports symlinks. + """ # When on a platform that doesn't implement it, calling os.symlink() (with the wrong number # of arguments) raises NotImplementedError, which allows us to gracefully check for the # feature. @@ -40,3 +88,20 @@ class DeletionOptions(GUIObject): # wrong number of arguments return True + @property + def link_deleted(self): + """Replace deleted dupes with symlinks (or hardlinks) to the dupe group reference. + + *bool*. *get/set* + + Whether the link is a symlink or hardlink is decided by :attr:`use_hardlinks`. + """ + return self._link_deleted + + @link_deleted.setter + def link_deleted(self, value): + self._link_deleted = value + hardlinks_enabled = value and self.supports_links() + self.view.set_hardlink_option_enabled(hardlinks_enabled) + + diff --git a/help/en/developer/core/gui.rst b/help/en/developer/core/gui.rst deleted file mode 100644 index 6db5cafd..00000000 --- a/help/en/developer/core/gui.rst +++ /dev/null @@ -1,5 +0,0 @@ -core.gui -======== - -.. automodule:: core.gui - :members: \ No newline at end of file diff --git a/help/en/developer/core/gui/deletion_options.rst b/help/en/developer/core/gui/deletion_options.rst new file mode 100644 index 00000000..2fbc7f8a --- /dev/null +++ b/help/en/developer/core/gui/deletion_options.rst @@ -0,0 +1,5 @@ +core.gui.deletion_options +========================= + +.. automodule:: core.gui.deletion_options + :members: diff --git a/help/en/developer/core/gui/index.rst b/help/en/developer/core/gui/index.rst new file mode 100644 index 00000000..0298f4b9 --- /dev/null +++ b/help/en/developer/core/gui/index.rst @@ -0,0 +1,10 @@ +core.gui +======== + +.. automodule:: core.gui + :members: + +.. toctree:: + :maxdepth: 2 + + deletion_options diff --git a/help/en/developer/core/index.rst b/help/en/developer/core/index.rst index 4e18ee06..8c88e7e1 100644 --- a/help/en/developer/core/index.rst +++ b/help/en/developer/core/index.rst @@ -9,4 +9,4 @@ core engine directories results - gui + gui/index diff --git a/qt/base/deletion_options.py b/qt/base/deletion_options.py index e42234d1..1ef204ab 100644 --- a/qt/base/deletion_options.py +++ b/qt/base/deletion_options.py @@ -22,6 +22,7 @@ class DeletionOptions(QDialog): self._setupUi() self.model.view = self + self.linkCheckbox.stateChanged.connect(self.linkCheckboxChanged) self.buttonBox.accepted.connect(self.accept) self.buttonBox.rejected.connect(self.reject) @@ -42,7 +43,6 @@ class DeletionOptions(QDialog): self.verticalLayout.addWidget(self.linkTypeRadio) if not self.model.supports_links(): self.linkCheckbox.setEnabled(False) - self.linkTypeRadio.setEnabled(False) self.linkCheckbox.setText(self.linkCheckbox.text() + tr(" (unsupported)")) self.directCheckbox = QCheckBox(tr("Directly delete files")) self.verticalLayout.addWidget(self.directCheckbox) @@ -56,8 +56,12 @@ class DeletionOptions(QDialog): self.buttonBox.addButton(tr("Cancel"), QDialogButtonBox.RejectRole) self.verticalLayout.addWidget(self.buttonBox) + #--- Signals + def linkCheckboxChanged(self, changed: int): + self.model.link_deleted = bool(changed) + #--- model --> view - def update_msg(self, msg): + def update_msg(self, msg: str): self.msgLabel.setText(msg) def show(self): @@ -70,3 +74,6 @@ class DeletionOptions(QDialog): self.model.direct = self.directCheckbox.isChecked() return result == QDialog.Accepted + def set_hardlink_option_enabled(self, is_enabled: bool): + self.linkTypeRadio.setEnabled(is_enabled) +