mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-06-23 15:30:45 -04:00
feat: multi-select support in toc editor
This commit is contained in:
parent
e4ab29ac03
commit
d2deef5be0
@ -532,102 +532,227 @@ class TreeWidget(QTreeWidget): # {{{
|
|||||||
self.setCurrentItem(item, 0, QItemSelectionModel.SelectionFlag.ClearAndSelect)
|
self.setCurrentItem(item, 0, QItemSelectionModel.SelectionFlag.ClearAndSelect)
|
||||||
self.scrollToItem(item)
|
self.scrollToItem(item)
|
||||||
|
|
||||||
def check_multi_selection(self):
|
def _sort_items_by_index(self, items, reverse=False):
|
||||||
if len(self.selectedItems()) > 1:
|
def get_index(item):
|
||||||
info_dialog(self, _('Multiple items selected'), _(
|
result = []
|
||||||
'You are trying to move multiple items at once, this is not supported. Instead use'
|
|
||||||
' Drag and Drop to move multiple items'), show=True)
|
parent = item.parent()
|
||||||
return False
|
while parent is not None:
|
||||||
return True
|
result.append(parent.indexOfChild(item))
|
||||||
|
item, parent = parent, parent.parent()
|
||||||
|
|
||||||
|
result.reverse()
|
||||||
|
return result
|
||||||
|
|
||||||
|
items.sort(key=get_index, reverse=reverse)
|
||||||
|
|
||||||
|
def _get_root_items(self, items):
|
||||||
|
items_ = []
|
||||||
|
for item in items:
|
||||||
|
if item is None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
parent = item
|
||||||
|
while (parent := parent.parent()) is not None:
|
||||||
|
if parent in items:
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
items_.append(item)
|
||||||
|
|
||||||
|
return items_
|
||||||
|
|
||||||
|
def _move_indent_left(self, items):
|
||||||
|
self._sort_items_by_index(items)
|
||||||
|
|
||||||
|
for item in items:
|
||||||
|
old_parent = item.parent()
|
||||||
|
if old_parent is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
old_index = old_parent.indexOfChild(item)
|
||||||
|
was_expanded = item.isExpanded() or item.childCount() == 0
|
||||||
|
|
||||||
|
new_parent = old_parent.parent() or self.invisibleRootItem()
|
||||||
|
new_index = new_parent.indexOfChild(old_parent) + 1
|
||||||
|
|
||||||
|
# all former lower siblings become children of indented item
|
||||||
|
for _ in range(old_parent.childCount() - old_index - 1):
|
||||||
|
lower_sibling = old_parent.child(old_index+1)
|
||||||
|
old_parent.removeChild(lower_sibling)
|
||||||
|
item.addChild(lower_sibling)
|
||||||
|
|
||||||
|
if lower_sibling not in items:
|
||||||
|
was_expanded = True
|
||||||
|
|
||||||
|
old_parent.removeChild(item)
|
||||||
|
new_parent.insertChild(new_index, item)
|
||||||
|
|
||||||
|
self.expandItem(new_parent)
|
||||||
|
if was_expanded:
|
||||||
|
self.expandItem(item)
|
||||||
|
|
||||||
|
def _move_indent_right(self, items):
|
||||||
|
self._sort_items_by_index(items)
|
||||||
|
|
||||||
|
failed_parent = None
|
||||||
|
for item in items:
|
||||||
|
# indent right == become child of upper sibling
|
||||||
|
old_parent = item.parent() or self.invisibleRootItem()
|
||||||
|
old_idx = old_parent.indexOfChild(item)
|
||||||
|
was_expanded = item.isExpanded()
|
||||||
|
|
||||||
|
if old_idx <= 0:
|
||||||
|
# no upper sibling exists; cannot become child
|
||||||
|
failed_parent = old_parent
|
||||||
|
continue
|
||||||
|
elif failed_parent and old_parent is failed_parent:
|
||||||
|
# this prevents siblings at the same level from
|
||||||
|
# nesting into each other forming a "staircase"
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
failed_parent = None
|
||||||
|
|
||||||
|
new_parent = old_parent.child(old_idx-1)
|
||||||
|
old_parent.removeChild(item)
|
||||||
|
new_parent.addChild(item)
|
||||||
|
|
||||||
|
self.expandItem(new_parent)
|
||||||
|
if was_expanded:
|
||||||
|
self.expandItem(item)
|
||||||
|
|
||||||
|
def _move_indent(self, items, indent):
|
||||||
|
if not items or indent == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
# indent offsets are absolute (as opposed to relative to parent)
|
||||||
|
# child indented with parent automatically, no need for manual
|
||||||
|
items_ = self._get_root_items(items)
|
||||||
|
if len(items_) <= 0 or indent == 0:
|
||||||
|
return
|
||||||
|
|
||||||
|
focus_item = self.currentItem()
|
||||||
|
|
||||||
|
self.push_history()
|
||||||
|
if indent < 0:
|
||||||
|
self._move_indent_left(items_)
|
||||||
|
elif indent > 0:
|
||||||
|
self._move_indent_right(items_)
|
||||||
|
|
||||||
|
# restore previous focused item
|
||||||
|
if focus_item is None and items_:
|
||||||
|
focus_item = items_[0]
|
||||||
|
|
||||||
|
if focus_item:
|
||||||
|
self.setCurrentItem(
|
||||||
|
focus_item,
|
||||||
|
0,
|
||||||
|
QItemSelectionModel.SelectionFlag.ClearAndSelect
|
||||||
|
)
|
||||||
|
|
||||||
|
for item in items:
|
||||||
|
item.setSelected(True)
|
||||||
|
|
||||||
def move_left(self):
|
def move_left(self):
|
||||||
if not self.check_multi_selection():
|
selected_items = self.selectedItems()
|
||||||
|
if len(selected_items) <= 0:
|
||||||
return
|
return
|
||||||
self.push_history()
|
|
||||||
item = self.currentItem()
|
self._move_indent(selected_items, -1)
|
||||||
if item is not None:
|
|
||||||
parent = item.parent()
|
|
||||||
if parent is not None:
|
|
||||||
is_expanded = item.isExpanded() or item.childCount() == 0
|
|
||||||
gp = parent.parent() or self.invisibleRootItem()
|
|
||||||
idx = gp.indexOfChild(parent)
|
|
||||||
for gc in [parent.child(i) for i in range(parent.indexOfChild(item)+1, parent.childCount())]:
|
|
||||||
parent.removeChild(gc)
|
|
||||||
item.addChild(gc)
|
|
||||||
parent.removeChild(item)
|
|
||||||
gp.insertChild(idx+1, item)
|
|
||||||
if is_expanded:
|
|
||||||
self.expandItem(item)
|
|
||||||
self.highlight_item(item)
|
|
||||||
|
|
||||||
def move_right(self):
|
def move_right(self):
|
||||||
if not self.check_multi_selection():
|
selected_items = self.selectedItems()
|
||||||
|
if len(selected_items) <= 0:
|
||||||
return
|
return
|
||||||
self.push_history()
|
|
||||||
item = self.currentItem()
|
self._move_indent(selected_items, 1)
|
||||||
if item is not None:
|
|
||||||
parent = item.parent() or self.invisibleRootItem()
|
|
||||||
idx = parent.indexOfChild(item)
|
|
||||||
if idx > 0:
|
|
||||||
is_expanded = item.isExpanded()
|
|
||||||
np = parent.child(idx-1)
|
|
||||||
parent.removeChild(item)
|
|
||||||
np.addChild(item)
|
|
||||||
if is_expanded:
|
|
||||||
self.expandItem(item)
|
|
||||||
self.highlight_item(item)
|
|
||||||
|
|
||||||
def move_down(self):
|
def move_down(self):
|
||||||
if not self.check_multi_selection():
|
selected_items = self.selectedItems()
|
||||||
|
if len(selected_items) <= 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.push_history()
|
self.push_history()
|
||||||
item = self.currentItem()
|
|
||||||
if item is None:
|
items_ = self._get_root_items(selected_items)
|
||||||
if self.root.childCount() == 0:
|
self._sort_items_by_index(items_)
|
||||||
return
|
|
||||||
item = self.root.child(0)
|
focus_item = self.currentItem()
|
||||||
self.highlight_item(item)
|
|
||||||
return
|
for item in reversed(items_):
|
||||||
parent = item.parent() or self.root
|
old_parent = item.parent() or self.invisibleRootItem()
|
||||||
idx = parent.indexOfChild(item)
|
old_index = old_parent.indexOfChild(item)
|
||||||
if idx == parent.childCount() - 1:
|
was_expanded = item.isExpanded() or item.childCount() == 0
|
||||||
# At end of parent, need to become sibling of parent
|
|
||||||
if parent is self.root:
|
new_parent = None
|
||||||
return
|
if old_index + 1 < old_parent.childCount():
|
||||||
gp = parent.parent() or self.root
|
# there is still space in parent; move down in same parent
|
||||||
parent.removeChild(item)
|
old_parent.removeChild(item)
|
||||||
gp.insertChild(gp.indexOfChild(parent)+1, item)
|
old_parent.insertChild(old_index + 1, item)
|
||||||
else:
|
elif old_parent is not self.invisibleRootItem():
|
||||||
sibling = parent.child(idx+1)
|
# move down past bottom of parent, become child of grandparent
|
||||||
parent.removeChild(item)
|
new_parent = old_parent.parent() or self.invisibleRootItem()
|
||||||
sibling.insertChild(0, item)
|
old_parent.removeChild(item)
|
||||||
self.highlight_item(item)
|
new_index = new_parent.indexOfChild(old_parent) + 1
|
||||||
|
new_parent.insertChild(new_index, item)
|
||||||
|
self.expandItem(new_parent)
|
||||||
|
|
||||||
|
self.expandItem(new_parent or old_parent)
|
||||||
|
if was_expanded:
|
||||||
|
self.expandItem(item)
|
||||||
|
|
||||||
|
if focus_item:
|
||||||
|
self.setCurrentItem(
|
||||||
|
focus_item,
|
||||||
|
0,
|
||||||
|
QItemSelectionModel.SelectionFlag.ClearAndSelect
|
||||||
|
)
|
||||||
|
|
||||||
|
for item in selected_items:
|
||||||
|
item.setSelected(True)
|
||||||
|
|
||||||
def move_up(self):
|
def move_up(self):
|
||||||
if not self.check_multi_selection():
|
selected_items = self.selectedItems()
|
||||||
|
if len(selected_items) <= 0:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.push_history()
|
self.push_history()
|
||||||
item = self.currentItem()
|
|
||||||
if item is None:
|
items_ = self._get_root_items(selected_items)
|
||||||
if self.root.childCount() == 0:
|
self._sort_items_by_index(items_)
|
||||||
return
|
|
||||||
item = self.root.child(self.root.childCount()-1)
|
focus_item = self.currentItem()
|
||||||
self.highlight_item(item)
|
|
||||||
return
|
for item in items_:
|
||||||
parent = item.parent() or self.root
|
old_parent = item.parent() or self.invisibleRootItem()
|
||||||
idx = parent.indexOfChild(item)
|
old_index = old_parent.indexOfChild(item)
|
||||||
if idx == 0:
|
was_expanded = item.isExpanded() or item.childCount() == 0
|
||||||
# At end of parent, need to become sibling of parent
|
|
||||||
if parent is self.root:
|
new_parent = None
|
||||||
return
|
if old_index - 1 >= 0:
|
||||||
gp = parent.parent() or self.root
|
# there is still space in parent; move up within parent
|
||||||
parent.removeChild(item)
|
old_parent.removeChild(item)
|
||||||
gp.insertChild(gp.indexOfChild(parent), item)
|
old_parent.insertChild(old_index - 1, item)
|
||||||
else:
|
elif old_parent is not self.invisibleRootItem():
|
||||||
sibling = parent.child(idx-1)
|
# move up past top of parent, become upper sibling of parent
|
||||||
parent.removeChild(item)
|
new_parent = old_parent.parent() or self.invisibleRootItem()
|
||||||
sibling.addChild(item)
|
old_parent.removeChild(item)
|
||||||
self.highlight_item(item)
|
new_index = new_parent.indexOfChild(old_parent)
|
||||||
|
new_parent.insertChild(new_index, item)
|
||||||
|
self.expandItem(new_parent)
|
||||||
|
|
||||||
|
self.expandItem(new_parent or old_parent)
|
||||||
|
if was_expanded:
|
||||||
|
self.expandItem(item)
|
||||||
|
|
||||||
|
if focus_item:
|
||||||
|
self.setCurrentItem(
|
||||||
|
focus_item,
|
||||||
|
0,
|
||||||
|
QItemSelectionModel.SelectionFlag.ClearAndSelect
|
||||||
|
)
|
||||||
|
|
||||||
|
for item in selected_items:
|
||||||
|
item.setSelected(True)
|
||||||
|
|
||||||
def del_items(self):
|
def del_items(self):
|
||||||
self.push_history()
|
self.push_history()
|
||||||
|
Loading…
x
Reference in New Issue
Block a user