Make drag & drop work again in the tag browser. Fix problem w/Qt near the end of the list.

This commit is contained in:
Charles Haley 2012-07-03 19:46:26 +02:00
parent ab16d1c0a8
commit 7dfa9897d9
2 changed files with 57 additions and 14 deletions

View File

@ -335,7 +335,6 @@ class TagsModel(QAbstractItemModel): # {{{
node.is_gst = is_gst node.is_gst = is_gst
if not is_gst: if not is_gst:
node.tag.is_hierarchical = '5state' node.tag.is_hierarchical = '5state'
if not is_gst:
tree_root[p] = {} tree_root[p] = {}
tree_root = tree_root[p] tree_root = tree_root[p]
else: else:
@ -519,7 +518,7 @@ class TagsModel(QAbstractItemModel): # {{{
# category display order is important here. The following works # category display order is important here. The following works
# only if all the non-user categories are displayed before the # only if all the non-user categories are displayed before the
# user categories # user categories
if category_is_hierarchical: if category_is_hierarchical or tag.is_hierarchical:
components = get_name_components(tag.original_name) components = get_name_components(tag.original_name)
else: else:
components = [tag.original_name] components = [tag.original_name]
@ -581,6 +580,14 @@ class TagsModel(QAbstractItemModel): # {{{
return [(t.tag.id, t.tag.original_name, t.tag.count) return [(t.tag.id, t.tag.original_name, t.tag.count)
for t in cat.child_tags() if t.tag.count > 0] for t in cat.child_tags() if t.tag.count > 0]
def is_in_user_category(self, index):
if not index.isValid():
return False
p = self.get_node(index)
while p.type != TagTreeItem.CATEGORY:
p = p.parent
return p.tag.category.startswith('@')
# Drag'n Drop {{{ # Drag'n Drop {{{
def mimeTypes(self): def mimeTypes(self):
return ["application/calibre+from_library", return ["application/calibre+from_library",
@ -646,13 +653,13 @@ class TagsModel(QAbstractItemModel): # {{{
action is Qt.CopyAction or Qt.MoveAction action is Qt.CopyAction or Qt.MoveAction
''' '''
def process_source_node(user_cats, src_parent, src_parent_is_gst, def process_source_node(user_cats, src_parent, src_parent_is_gst,
is_uc, dest_key, node): is_uc, dest_key, idx):
''' '''
Copy/move an item and all its children to the destination Copy/move an item and all its children to the destination
''' '''
copied = False copied = False
src_name = node.tag.original_name src_name = idx.tag.original_name
src_cat = node.tag.category src_cat = idx.tag.category
# delete the item if the source is a user category and action is move # delete the item if the source is a user category and action is move
if is_uc and not src_parent_is_gst and src_parent in user_cats and \ if is_uc and not src_parent_is_gst and src_parent in user_cats and \
action == Qt.MoveAction: action == Qt.MoveAction:
@ -675,7 +682,7 @@ class TagsModel(QAbstractItemModel): # {{{
if add_it: if add_it:
user_cats[dest_key].append([src_name, src_cat, 0]) user_cats[dest_key].append([src_name, src_cat, 0])
for c in node.children: for c in idx.children:
copied = process_source_node(user_cats, src_parent, src_parent_is_gst, copied = process_source_node(user_cats, src_parent, src_parent_is_gst,
is_uc, dest_key, c) is_uc, dest_key, c)
return copied return copied
@ -696,11 +703,11 @@ class TagsModel(QAbstractItemModel): # {{{
if dest_key not in user_cats: if dest_key not in user_cats:
continue continue
node = self.index_for_path(path) idx = self.index_for_path(path)
if node: if idx.isValid():
process_source_node(user_cats, src_parent, src_parent_is_gst, process_source_node(user_cats, src_parent, src_parent_is_gst,
is_uc, dest_key, is_uc, dest_key,
self.get_node(node)) self.get_node(idx))
self.db.prefs.set('user_categories', user_cats) self.db.prefs.set('user_categories', user_cats)
self.refresh_required.emit() self.refresh_required.emit()
@ -1139,6 +1146,8 @@ class TagsModel(QAbstractItemModel): # {{{
return QModelIndex() return QModelIndex()
ans = self.createIndex(parent_item.row(), 0, parent_item) ans = self.createIndex(parent_item.row(), 0, parent_item)
if not ans.isValid():
return QModelIndex()
return ans return ans
def rowCount(self, parent): def rowCount(self, parent):

View File

@ -12,7 +12,8 @@ from functools import partial
from itertools import izip from itertools import izip
from PyQt4.Qt import (QStyledItemDelegate, Qt, QTreeView, pyqtSignal, QSize, from PyQt4.Qt import (QStyledItemDelegate, Qt, QTreeView, pyqtSignal, QSize,
QIcon, QApplication, QMenu, QPoint, QModelIndex, QToolTip, QCursor) QIcon, QApplication, QMenu, QPoint, QModelIndex, QToolTip, QCursor,
QDrag)
from calibre.gui2.tag_browser.model import (TagTreeItem, TAG_SEARCH_STATES, from calibre.gui2.tag_browser.model import (TagTreeItem, TAG_SEARCH_STATES,
TagsModel) TagsModel)
@ -101,6 +102,7 @@ class TagsView(QTreeView): # {{{
self.setDragEnabled(True) self.setDragEnabled(True)
self.setDragDropMode(self.DragDrop) self.setDragDropMode(self.DragDrop)
self.setDropIndicatorShown(True) self.setDropIndicatorShown(True)
self.in_drag_drop = False
self.setAutoExpandDelay(500) self.setAutoExpandDelay(500)
self.pane_is_visible = False self.pane_is_visible = False
self.search_icon = QIcon(I('search.png')) self.search_icon = QIcon(I('search.png'))
@ -232,10 +234,35 @@ class TagsView(QTreeView): # {{{
s = s if s else None s = s if s else None
self._model.set_search_restriction(s) self._model.set_search_restriction(s)
def mouseMoveEvent(self, event):
dex = self.indexAt(event.pos())
if self.in_drag_drop or not dex.isValid():
QTreeView.mouseMoveEvent(self, event)
return
# Must deal with odd case where the node being dragged is 'virtual',
# created to form a hierarchy. We can't really drag this node, but in
# addition we can't allow drag recognition to notice going over some
# other node and grabbing that one. So we set in_drag_drop to prevent
# this from happening, turning it off when the user lifts the button.
self.in_drag_drop = True
if not self._model.flags(dex) & Qt.ItemIsDragEnabled:
QTreeView.mouseMoveEvent(self, event)
return
md = self._model.mimeData([dex])
pixmap = dex.data(Qt.DecorationRole).toPyObject().pixmap(25, 25)
drag = QDrag(self)
drag.setPixmap(pixmap)
drag.setMimeData(md)
if self._model.is_in_user_category(dex):
drag.exec_(Qt.CopyAction|Qt.MoveAction, Qt.CopyAction)
else:
drag.exec_(Qt.CopyAction)
def mouseReleaseEvent(self, event): def mouseReleaseEvent(self, event):
# Swallow everything except leftButton so context menus work correctly # Swallow everything except leftButton so context menus work correctly
if event.button() == Qt.LeftButton: if event.button() == Qt.LeftButton or self.in_drag_drop:
QTreeView.mouseReleaseEvent(self, event) QTreeView.mouseReleaseEvent(self, event)
self.in_drag_drop = False
def mouseDoubleClickEvent(self, event): def mouseDoubleClickEvent(self, event):
# swallow these to avoid toggling and editing at the same time # swallow these to avoid toggling and editing at the same time
@ -639,12 +666,19 @@ class TagsView(QTreeView): # {{{
self.show_item_at_index(self._model.index_for_path(path), box=box, self.show_item_at_index(self._model.index_for_path(path), box=box,
position=position) position=position)
def expand_parent(self, idx, depth=0):
p = self._model.parent(idx)
d = 0
if p.isValid():
d = self.expand_parent(p, depth+1)
if d == 0:
self.expand(idx)
return d+1
def show_item_at_index(self, idx, box=False, def show_item_at_index(self, idx, box=False,
position=QTreeView.PositionAtCenter): position=QTreeView.PositionAtCenter):
if idx.isValid() and idx.data(Qt.UserRole).toPyObject() is not self._model.root_item: if idx.isValid() and idx.data(Qt.UserRole).toPyObject() is not self._model.root_item:
self.expand(self._model.parent(idx)) # Needed otherwise Qt sometimes segfaults if the self.expand_parent(idx)
# node is buried in a collapsed, off
# screen hierarchy
self.setCurrentIndex(idx) self.setCurrentIndex(idx)
self.scrollTo(idx, position) self.scrollTo(idx, position)
if box: if box: