Copy and Paste + Shortcuts (#79)

* Fixed merge conflicts

* fixed format?

* Improve readability (Apply suggestions from code review)

Co-authored-by: yed podtrzitko <yedpodtrzitko@users.noreply.github.com>

* bug fix: Copy last selected not first

* Fix copy entanglement; Fix paste overwriting

* Change multi-selection copy to merge data

- Multi-selection copy now merges fields of all selected entries
- Action states are now handled

---------

Co-authored-by: yed podtrzitko <yedpodtrzitko@users.noreply.github.com>
Co-authored-by: Travis Abendshien <46939827+CyanVoxel@users.noreply.github.com>
This commit is contained in:
Creepler13
2024-07-19 08:33:52 +02:00
committed by GitHub
parent bebca634de
commit aa0aad4300

View File

@@ -8,6 +8,7 @@
"""A Qt driver for TagStudio."""
import ctypes
import copy
import logging
import math
import os
@@ -64,6 +65,7 @@ from src.core.constants import (
TS_FOLDER_NAME,
VERSION_BRANCH,
VERSION,
TEXT_FIELDS,
TAG_FAVORITE,
TAG_ARCHIVED,
)
@@ -299,6 +301,9 @@ class QtDriver(QObject):
icon.addFile(str(icon_path))
app.setWindowIcon(icon)
self.copied_fields: list[dict] = []
self.is_buffer_merged: bool = False
menu_bar = QMenuBar(self.main_window)
self.main_window.setMenuBar(menu_bar)
menu_bar.setNativeMenuBar(True)
@@ -392,6 +397,36 @@ class QtDriver(QObject):
edit_menu.addSeparator()
# NOTE: Name is set in update_clipboard_actions()
self.copy_entry_fields_action = QAction(menu_bar)
self.copy_entry_fields_action.triggered.connect(
lambda: self.copy_entry_fields_callback()
)
self.copy_entry_fields_action.setShortcut(
QtCore.QKeyCombination(
QtCore.Qt.KeyboardModifier(QtCore.Qt.KeyboardModifier.ControlModifier),
QtCore.Qt.Key.Key_C,
)
)
self.copy_entry_fields_action.setToolTip("Ctrl+C")
edit_menu.addAction(self.copy_entry_fields_action)
# NOTE: Name is set in update_clipboard_actions()
self.paste_entry_fields_action = QAction(menu_bar)
self.paste_entry_fields_action.triggered.connect(
self.paste_entry_fields_callback
)
self.paste_entry_fields_action.setShortcut(
QtCore.QKeyCombination(
QtCore.Qt.KeyboardModifier(QtCore.Qt.KeyboardModifier.ControlModifier),
QtCore.Qt.Key.Key_V,
)
)
self.paste_entry_fields_action.setToolTip("Ctrl+V")
edit_menu.addAction(self.paste_entry_fields_action)
edit_menu.addSeparator()
select_all_action = QAction("Select All", menu_bar)
select_all_action.triggered.connect(self.select_all_action_callback)
select_all_action.setShortcut(
@@ -505,6 +540,8 @@ class QtDriver(QObject):
help_menu.addAction(self.repo_action)
self.set_macro_menu_viability()
self.update_clipboard_actions()
menu_bar.addMenu(file_menu)
menu_bar.addMenu(edit_menu)
menu_bar.addMenu(tools_menu)
@@ -700,6 +737,10 @@ class QtDriver(QObject):
self.cur_frame_idx = -1
self.cur_query = ""
self.selected.clear()
self.copied_fields.clear()
self.is_buffer_merged = False
self.update_clipboard_actions()
self.set_macro_menu_viability()
self.preview_panel.update_widgets()
self.filter_items()
self.main_window.toggle_landing_page(True)
@@ -950,6 +991,114 @@ class QtDriver(QObject):
mode="replace",
)
def copy_entry_fields_callback(self):
"""Copies fields from selected Entries into to buffer."""
merged_fields: list[dict] = []
merged_count: int = 0
for item_type, item_id in self.selected:
if item_type == ItemType.ENTRY:
entry = self.lib.get_entry(item_id)
if len(entry.fields) > 0:
merged_count += 1
for field in entry.fields:
field_id: int = self.lib.get_field_attr(field, "id")
content = self.lib.get_field_attr(field, "content")
if self.lib.get_field_obj(int(field_id))["type"] == "tag_box":
existing_fields: list[int] = self.lib.get_field_index_in_entry(
entry, field_id
)
if existing_fields and merged_fields:
for i in content:
field_index = copy.deepcopy(existing_fields[0])
if i not in merged_fields[field_index][field_id]:
merged_fields[field_index][field_id].append(
copy.deepcopy(i)
)
else:
merged_fields.append(copy.deepcopy({field_id: content}))
if self.lib.get_field_obj(int(field_id))["type"] in TEXT_FIELDS:
if {field_id: content} not in merged_fields:
merged_fields.append(copy.deepcopy({field_id: content}))
# Only set merged state to True if multiple Entries with actual field data were copied.
if merged_count > 1:
self.is_buffer_merged = True
else:
self.is_buffer_merged = False
self.copied_fields = merged_fields
self.update_clipboard_actions()
def paste_entry_fields_callback(self):
"""Pastes buffered fields into currently selected Entries."""
# Code ported from ts_cli.py
if self.copied_fields:
for item_type, item_id in self.selected:
if item_type == ItemType.ENTRY:
entry = self.lib.get_entry(item_id)
for field in self.copied_fields:
field_id: int = self.lib.get_field_attr(field, "id")
content = self.lib.get_field_attr(field, "content")
if self.lib.get_field_obj(int(field_id))["type"] == "tag_box":
existing_fields: list[int] = (
self.lib.get_field_index_in_entry(entry, field_id)
)
if existing_fields:
self.lib.update_entry_field(
item_id, existing_fields[0], content, "append"
)
else:
self.lib.add_field_to_entry(item_id, field_id)
self.lib.update_entry_field(
item_id, -1, content, "append"
)
if self.lib.get_field_obj(int(field_id))["type"] in TEXT_FIELDS:
if not self.lib.does_field_content_exist(
item_id, field_id, content
):
self.lib.add_field_to_entry(item_id, field_id)
self.lib.update_entry_field(
item_id, -1, content, "replace"
)
self.preview_panel.update_widgets()
self.update_badges()
self.update_clipboard_actions()
def update_clipboard_actions(self):
"""Updates the text and enabled state of the field copy & paste actions."""
# Buffer State Dependant
if self.copied_fields:
self.paste_entry_fields_action.setDisabled(False)
else:
self.paste_entry_fields_action.setDisabled(True)
self.paste_entry_fields_action.setText("&Paste Fields")
# Selection Count Dependant
if len(self.selected) <= 0:
self.copy_entry_fields_action.setDisabled(True)
self.paste_entry_fields_action.setDisabled(True)
self.copy_entry_fields_action.setText("&Copy Fields")
if len(self.selected) == 1:
self.copy_entry_fields_action.setDisabled(False)
self.copy_entry_fields_action.setText("&Copy Fields")
elif len(self.selected) > 1:
self.copy_entry_fields_action.setDisabled(False)
self.copy_entry_fields_action.setText("&Copy Combined Fields")
# Merged State Dependant
if self.is_buffer_merged:
self.paste_entry_fields_action.setText("&Paste Combined Fields")
else:
self.paste_entry_fields_action.setText("&Paste Fields")
def mouse_navigation(self, event: QMouseEvent):
# print(event.button())
if event.button() == Qt.MouseButton.ForwardButton:
@@ -1197,6 +1346,7 @@ class QtDriver(QObject):
self.preview_panel.set_tags_updated_slot(it.update_badges)
self.set_macro_menu_viability()
self.update_clipboard_actions()
self.preview_panel.update_widgets()
def set_macro_menu_viability(self):