Drag 'n drop re-ordering of spine items

This commit is contained in:
Kovid Goyal 2013-10-19 11:15:30 +05:30
parent 26c1fd0689
commit 7f2a1f99da
3 changed files with 49 additions and 2 deletions

View File

@ -355,6 +355,28 @@ class Container(object): # {{{
for name in nixed: for name in nixed:
self.remove_item(name) self.remove_item(name)
def set_spine(self, spine_items):
''' Set the spine to be spine_items where spine_items is an iterable of
the form (name, linear). Will raise an error if one of the names is not
present in the manifest. '''
imap = self.manifest_id_map
imap = {name:item_id for item_id, name in imap.iteritems()}
items = [item for item, name, linear in self.spine_iter]
tail, last_tail = (items[0].tail, items[-1].tail) if items else ('\n ', '\n ')
map(self.remove_from_xml, items)
spine = self.opf_xpath('//opf:spine')[0]
spine.text = tail
for name, linear in spine_items:
i = spine.makeelement('{%s}itemref' % OPF_NAMESPACES['opf'], nsmap={'opf':OPF_NAMESPACES['opf']})
i.tail = tail
i.set('idref', imap[name])
spine.append(i)
if not linear:
i.set('linear', 'no')
if len(spine) > 0:
spine[-1].tail = last_tail
self.dirty(self.opf_name)
def remove_item(self, name): def remove_item(self, name):
''' '''
Remove the item identified by name from this container. This removes all Remove the item identified by name from this container. This removes all

View File

@ -35,6 +35,7 @@ class Boss(QObject):
def __call__(self, gui): def __call__(self, gui):
self.gui = gui self.gui = gui
gui.file_list.delete_requested.connect(self.delete_requested) gui.file_list.delete_requested.connect(self.delete_requested)
gui.file_list.reorder_spine.connect(self.reorder_spine)
def mkdtemp(self): def mkdtemp(self):
self.container_count += 1 self.container_count += 1
@ -130,6 +131,16 @@ class Boss(QObject):
self.gui.file_list.delete_done(spine_items, other_items) self.gui.file_list.delete_done(spine_items, other_items)
# TODO: Update other GUI elements # TODO: Update other GUI elements
def reorder_spine(self, items):
# TODO: If content.opf is dirty in an editor, abort, calling
# file_list.build(current_container) to undo drag and drop
self.add_savepoint(_('Re-order text'))
c = current_container()
c.set_spine(items)
self.gui.action_save.setEnabled(True)
self.gui.file_list.build(current_container()) # needed as the linear flag may have changed on some items
# TODO: If content.opf is open in an editor, reload it
def save_book(self): def save_book(self):
self.gui.action_save.setEnabled(False) self.gui.action_save.setEnabled(False)
tdir = tempfile.mkdtemp(prefix='save-%05d-' % self.container_count, dir=self.tdir) tdir = tempfile.mkdtemp(prefix='save-%05d-' % self.container_count, dir=self.tdir)

View File

@ -22,6 +22,7 @@ from calibre.utils.icu import sort_key
TOP_ICON_SIZE = 24 TOP_ICON_SIZE = 24
NAME_ROLE = Qt.UserRole NAME_ROLE = Qt.UserRole
CATEGORY_ROLE = NAME_ROLE + 1 CATEGORY_ROLE = NAME_ROLE + 1
LINEAR_ROLE = CATEGORY_ROLE + 1
NBSP = '\xa0' NBSP = '\xa0'
class ItemDelegate(QStyledItemDelegate): # {{{ class ItemDelegate(QStyledItemDelegate): # {{{
@ -56,6 +57,7 @@ class ItemDelegate(QStyledItemDelegate): # {{{
class FileList(QTreeWidget): class FileList(QTreeWidget):
delete_requested = pyqtSignal(object, object) delete_requested = pyqtSignal(object, object)
reorder_spine = pyqtSignal(object)
def __init__(self, parent=None): def __init__(self, parent=None):
QTreeWidget.__init__(self, parent) QTreeWidget.__init__(self, parent)
@ -211,6 +213,7 @@ class FileList(QTreeWidget):
item.setStatusTip(0, _('Full path: ') + name) item.setStatusTip(0, _('Full path: ') + name)
item.setData(0, NAME_ROLE, name) item.setData(0, NAME_ROLE, name)
item.setData(0, CATEGORY_ROLE, category) item.setData(0, CATEGORY_ROLE, category)
item.setData(0, LINEAR_ROLE, bool(linear))
set_display_name(name, item) set_display_name(name, item)
# TODO: Add appropriate tooltips based on the emblems # TODO: Add appropriate tooltips based on the emblems
emblems = [] emblems = []
@ -294,11 +297,22 @@ class FileList(QTreeWidget):
super(FileList, self).dropEvent(event) super(FileList, self).dropEvent(event)
current_order = {text.child(i):i for i in xrange(text.childCount())} current_order = {text.child(i):i for i in xrange(text.childCount())}
if current_order != pre_drop_order: if current_order != pre_drop_order:
pass # TODO: Implement this order = []
for child in (text.child(i) for i in xrange(text.childCount())):
name = unicode(child.data(0, NAME_ROLE).toString())
linear = child.data(0, LINEAR_ROLE).toBool()
order.append([name, linear])
# Ensure that all non-linear items are at the end, any non-linear
# items not at the end will be made linear
for i, (name, linear) in tuple(enumerate(order)):
if not linear and i < len(order) - 1 and order[i+1][1]:
order[i][1] = True
self.reorder_spine.emit(order)
class FileListWidget(QWidget): class FileListWidget(QWidget):
delete_requested = pyqtSignal(object, object) delete_requested = pyqtSignal(object, object)
reorder_spine = pyqtSignal(object)
def __init__(self, parent=None): def __init__(self, parent=None):
QWidget.__init__(self, parent) QWidget.__init__(self, parent)
@ -306,7 +320,7 @@ class FileListWidget(QWidget):
self.file_list = FileList(self) self.file_list = FileList(self)
self.layout().addWidget(self.file_list) self.layout().addWidget(self.file_list)
self.layout().setContentsMargins(0, 0, 0, 0) self.layout().setContentsMargins(0, 0, 0, 0)
for x in ('delete_requested',): for x in ('delete_requested', 'reorder_spine'):
getattr(self.file_list, x).connect(getattr(self, x)) getattr(self.file_list, x).connect(getattr(self, x))
for x in ('delete_done',): for x in ('delete_done',):
setattr(self, x, getattr(self.file_list, x)) setattr(self, x, getattr(self.file_list, x))