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.
This commit is contained in:
Virgil Dupras 2013-12-06 15:48:01 -05:00
parent 7116674663
commit cb8bb5a70e
6 changed files with 95 additions and 13 deletions

View File

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

View File

@ -1,5 +0,0 @@
core.gui
========
.. automodule:: core.gui
:members:

View File

@ -0,0 +1,5 @@
core.gui.deletion_options
=========================
.. automodule:: core.gui.deletion_options
:members:

View File

@ -0,0 +1,10 @@
core.gui
========
.. automodule:: core.gui
:members:
.. toctree::
:maxdepth: 2
deletion_options

View File

@ -9,4 +9,4 @@ core
engine
directories
results
gui
gui/index

View File

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