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) +