%PDF- %PDF-
| Direktori : /lib/calibre/calibre/gui2/preferences/ |
| Current File : //lib/calibre/calibre/gui2/preferences/toolbar.py |
#!/usr/bin/env python3
__license__ = 'GPL v3'
__copyright__ = '2010, Kovid Goyal <kovid@kovidgoyal.net>'
__docformat__ = 'restructuredtext en'
from qt.core import QAbstractListModel, Qt, QIcon, QItemSelectionModel
from calibre import force_unicode
from calibre.gui2.preferences.toolbar_ui import Ui_Form
from calibre.gui2 import gprefs, warning_dialog, error_dialog
from calibre.gui2.preferences import ConfigWidgetBase, test_widget, AbortCommit
from calibre.utils.icu import primary_sort_key
def sort_key_for_action(ac):
q = getattr(ac, 'action_spec', None)
try:
q = ac.name if q is None else q[0]
return primary_sort_key(force_unicode(q))
except Exception:
return primary_sort_key('')
class FakeAction:
def __init__(self, name, gui_name, icon, tooltip=None,
dont_add_to=frozenset(), dont_remove_from=frozenset()):
self.name = name
self.action_spec = (gui_name, icon, tooltip, None)
self.dont_remove_from = dont_remove_from
self.dont_add_to = dont_add_to
class BaseModel(QAbstractListModel):
def name_to_action(self, name, gui):
if name == 'Donate':
return FakeAction(
'Donate', _('Donate'), 'donate.png', tooltip=_('Donate to support the development of calibre'),
dont_add_to=frozenset(['context-menu', 'context-menu-device']))
if name == 'Location Manager':
return FakeAction('Location Manager', _('Location Manager'), 'reader.png',
_('Switch between library and device views'),
dont_add_to=frozenset(['menubar', 'toolbar',
'toolbar-child', 'context-menu',
'context-menu-device']))
if name is None:
return FakeAction('--- '+('Separator')+' ---',
'--- '+_('Separator')+' ---', None,
dont_add_to=frozenset(['menubar', 'menubar-device']))
try:
return gui.iactions[name]
except:
return None
def rowCount(self, parent):
return len(self._data)
def data(self, index, role):
row = index.row()
action = self._data[row].action_spec
if role == Qt.ItemDataRole.DisplayRole:
text = action[0]
text = text.replace('&', '')
if text == _('%d books'):
text = _('Choose library')
return (text)
if role == Qt.ItemDataRole.DecorationRole:
if hasattr(self._data[row], 'qaction'):
icon = self._data[row].qaction.icon()
if not icon.isNull():
return (icon)
ic = action[1]
if ic is None:
ic = 'blank.png'
return (QIcon(I(ic)))
if role == Qt.ItemDataRole.ToolTipRole and action[2] is not None:
return (action[2])
return None
def names(self, indexes):
rows = [i.row() for i in indexes]
ans = []
for i in rows:
n = self._data[i].name
if n.startswith('---'):
n = None
ans.append(n)
return ans
def has_action(self, name):
for a in self._data:
if a.name == name:
return True
return False
class AllModel(BaseModel):
def __init__(self, key, gui):
BaseModel.__init__(self)
self.gprefs_name = 'action-layout-'+key
current = gprefs[self.gprefs_name]
self.gui = gui
self.key = key
self._data = self.get_all_actions(current)
def get_all_actions(self, current):
all = list(self.gui.iactions.keys()) + ['Donate', 'Location Manager']
all = [x for x in all if x not in current] + [None]
all = [self.name_to_action(x, self.gui) for x in all]
all = [x for x in all if self.key not in x.dont_add_to]
all.sort(key=sort_key_for_action)
return all
def add(self, names):
actions = []
for name in names:
if name is None or name.startswith('---'):
continue
actions.append(self.name_to_action(name, self.gui))
self.beginResetModel()
self._data.extend(actions)
self._data.sort(key=sort_key_for_action)
self.endResetModel()
def remove(self, indices, allowed):
rows = [i.row() for i in indices]
remove = set()
for row in rows:
ac = self._data[row]
if ac.name.startswith('---'):
continue
if ac.name in allowed:
remove.add(row)
ndata = []
for i, ac in enumerate(self._data):
if i not in remove:
ndata.append(ac)
self.beginResetModel()
self._data = ndata
self.endResetModel()
def restore_defaults(self):
current = gprefs.defaults[self.gprefs_name]
self.beginResetModel()
self._data = self.get_all_actions(current)
self.endResetModel()
class CurrentModel(BaseModel):
def __init__(self, key, gui):
BaseModel.__init__(self)
self.gprefs_name = 'action-layout-'+key
current = gprefs[self.gprefs_name]
self._data = [self.name_to_action(x, gui) for x in current]
self._data = [x for x in self._data if x is not None]
self.key = key
self.gui = gui
def move(self, idx, delta):
row = idx.row()
nrow = (row + delta + len(self._data)) % len(self._data)
if nrow < 0 or nrow >= len(self._data):
return
t = self._data[row]
self._data[row] = self._data[nrow]
self._data[nrow] = t
ni = self.index(nrow)
self.dataChanged.emit(idx, idx)
self.dataChanged.emit(ni, ni)
return ni
def move_many(self, indices, delta):
indices = sorted(indices, key=lambda i: i.row(), reverse=delta > 0)
ans = {}
for idx in indices:
ni = self.move(idx, delta)
ans[idx.row()] = ni
return ans
def add(self, names):
actions = []
reject = set()
for name in names:
ac = self.name_to_action(name, self.gui)
if self.key in ac.dont_add_to:
reject.add(ac)
else:
actions.append(ac)
self.beginResetModel()
self._data.extend(actions)
self.endResetModel()
return reject
def remove(self, indices):
rows = [i.row() for i in indices]
remove, rejected = set(), set()
for row in rows:
ac = self._data[row]
if self.key in ac.dont_remove_from:
rejected.add(ac)
continue
remove.add(row)
ndata = []
for i, ac in enumerate(self._data):
if i not in remove:
ndata.append(ac)
self.beginResetModel()
self._data = ndata
self.endResetModel()
return rejected
def commit(self):
old = gprefs[self.gprefs_name]
new = []
for x in self._data:
n = x.name
if n.startswith('---'):
n = None
new.append(n)
new = tuple(new)
if new != old:
defaults = gprefs.defaults[self.gprefs_name]
if defaults == new:
del gprefs[self.gprefs_name]
else:
gprefs[self.gprefs_name] = new
def restore_defaults(self):
current = gprefs.defaults[self.gprefs_name]
self.beginResetModel()
self._data = [self.name_to_action(x, self.gui) for x in current]
self.endResetModel()
class ConfigWidget(ConfigWidgetBase, Ui_Form):
LOCATIONS = [
('toolbar', _('The main toolbar')),
('toolbar-device', _('The main toolbar when a device is connected')),
('toolbar-child', _('The optional second toolbar')),
('menubar', _('The menubar')),
('menubar-device', _('The menubar when a device is connected')),
('context-menu', _('The context menu for the books in the '
'calibre library')),
('context-menu-split', _('The context menu for the split book list')),
('context-menu-device', _('The context menu for the books on '
'the device')),
('context-menu-cover-browser', _('The context menu for the Cover '
'browser')),
]
def genesis(self, gui):
self.all_actions.doubleClicked.connect(self.add_single_action)
self.current_actions.doubleClicked.connect(self.remove_single_action)
self.models = {}
self.what.addItem(_('Click to choose toolbar or menu to customize'),
'blank')
for key, text in self.LOCATIONS:
self.what.addItem(text, key)
all_model = AllModel(key, gui)
current_model = CurrentModel(key, gui)
self.models[key] = (all_model, current_model)
self.what.setCurrentIndex(0)
self.what.currentIndexChanged[int].connect(self.what_changed)
self.what_changed(0)
self.add_action_button.clicked.connect(self.add_action)
self.remove_action_button.clicked.connect(self.remove_action)
connect_lambda(self.action_up_button.clicked, self, lambda self: self.move(-1))
connect_lambda(self.action_down_button.clicked, self, lambda self: self.move(1))
self.all_actions.setMouseTracking(True)
self.current_actions.setMouseTracking(True)
self.all_actions.entered.connect(self.all_entered)
self.current_actions.entered.connect(self.current_entered)
def all_entered(self, index):
tt = self.all_actions.model().data(index, Qt.ItemDataRole.ToolTipRole) or ''
self.help_text.setText(tt)
def current_entered(self, index):
tt = self.current_actions.model().data(index, Qt.ItemDataRole.ToolTipRole) or ''
self.help_text.setText(tt)
def what_changed(self, idx):
key = str(self.what.itemData(idx) or '')
if key == 'blank':
self.actions_widget.setVisible(False)
self.spacer_widget.setVisible(True)
else:
self.actions_widget.setVisible(True)
self.spacer_widget.setVisible(False)
self.all_actions.setModel(self.models[key][0])
self.current_actions.setModel(self.models[key][1])
def add_action(self, *args):
self._add_action(self.all_actions.selectionModel().selectedIndexes())
def add_single_action(self, index):
self._add_action([index])
def _add_action(self, indices):
names = self.all_actions.model().names(indices)
if names:
not_added = self.current_actions.model().add(names)
ns = {y.name for y in not_added}
added = set(names) - ns
self.all_actions.model().remove(indices, added)
if not_added:
warning_dialog(self, _('Cannot add'),
_('Cannot add the actions %s to this location') %
','.join([a.action_spec[0] for a in not_added]),
show=True)
if added:
ca = self.current_actions
idx = ca.model().index(ca.model().rowCount(None)-1)
ca.scrollTo(idx)
self.changed_signal.emit()
def remove_action(self, *args):
self._remove_action(self.current_actions.selectionModel().selectedIndexes())
def remove_single_action(self, index):
self._remove_action([index])
def _remove_action(self, indices):
names = self.current_actions.model().names(indices)
if names:
not_removed = self.current_actions.model().remove(indices)
ns = {y.name for y in not_removed}
removed = set(names) - ns
self.all_actions.model().add(removed)
if not_removed:
warning_dialog(self, _('Cannot remove'),
_('Cannot remove the actions %s from this location') %
','.join([a.action_spec[0] for a in not_removed]),
show=True)
else:
self.changed_signal.emit()
def move(self, delta, *args):
sm = self.current_actions.selectionModel()
x = sm.selectedIndexes()
if x and len(x):
i = sm.currentIndex().row()
m = self.current_actions.model()
idx_map = m.move_many(x, delta)
newci = idx_map.get(i)
if newci is not None:
sm.setCurrentIndex(newci, QItemSelectionModel.SelectionFlag.ClearAndSelect)
sm.clear()
for idx in idx_map.values():
sm.select(idx, QItemSelectionModel.SelectionFlag.Select)
self.changed_signal.emit()
def commit(self):
# Ensure preferences are showing in either the toolbar or
# the menubar.
pref_in_toolbar = self.models['toolbar'][1].has_action('Preferences')
pref_in_menubar = self.models['menubar'][1].has_action('Preferences')
lm_in_toolbar = self.models['toolbar-device'][1].has_action('Location Manager')
lm_in_menubar = self.models['menubar-device'][1].has_action('Location Manager')
if not pref_in_toolbar and not pref_in_menubar:
error_dialog(self, _('Preferences missing'), _(
'The Preferences action must be in either the main toolbar or the menubar.'), show=True)
raise AbortCommit()
if not lm_in_toolbar and not lm_in_menubar:
error_dialog(self, _('Location manager missing'), _(
'The Location manager must be in either the main toolbar or the menubar when a device is connected.'), show=True)
raise AbortCommit()
# Save data.
for am, cm in self.models.values():
cm.commit()
return False
def restore_defaults(self):
for am, cm in self.models.values():
cm.restore_defaults()
am.restore_defaults()
self.changed_signal.emit()
def refresh_gui(self, gui):
gui.bars_manager.init_bars()
gui.bars_manager.update_bars()
if __name__ == '__main__':
from calibre.gui2 import Application
app = Application([])
test_widget('Interface', 'Toolbar')