supervertaler 1.9.163__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- Supervertaler.py +48473 -0
- modules/__init__.py +10 -0
- modules/ai_actions.py +964 -0
- modules/ai_attachment_manager.py +343 -0
- modules/ai_file_viewer_dialog.py +210 -0
- modules/autofingers_engine.py +466 -0
- modules/cafetran_docx_handler.py +379 -0
- modules/config_manager.py +469 -0
- modules/database_manager.py +1911 -0
- modules/database_migrations.py +417 -0
- modules/dejavurtf_handler.py +779 -0
- modules/document_analyzer.py +427 -0
- modules/docx_handler.py +689 -0
- modules/encoding_repair.py +319 -0
- modules/encoding_repair_Qt.py +393 -0
- modules/encoding_repair_ui.py +481 -0
- modules/feature_manager.py +350 -0
- modules/figure_context_manager.py +340 -0
- modules/file_dialog_helper.py +148 -0
- modules/find_replace.py +164 -0
- modules/find_replace_qt.py +457 -0
- modules/glossary_manager.py +433 -0
- modules/image_extractor.py +188 -0
- modules/keyboard_shortcuts_widget.py +571 -0
- modules/llm_clients.py +1211 -0
- modules/llm_leaderboard.py +737 -0
- modules/llm_superbench_ui.py +1401 -0
- modules/local_llm_setup.py +1104 -0
- modules/model_update_dialog.py +381 -0
- modules/model_version_checker.py +373 -0
- modules/mqxliff_handler.py +638 -0
- modules/non_translatables_manager.py +743 -0
- modules/pdf_rescue_Qt.py +1822 -0
- modules/pdf_rescue_tkinter.py +909 -0
- modules/phrase_docx_handler.py +516 -0
- modules/project_home_panel.py +209 -0
- modules/prompt_assistant.py +357 -0
- modules/prompt_library.py +689 -0
- modules/prompt_library_migration.py +447 -0
- modules/quick_access_sidebar.py +282 -0
- modules/ribbon_widget.py +597 -0
- modules/sdlppx_handler.py +874 -0
- modules/setup_wizard.py +353 -0
- modules/shortcut_manager.py +932 -0
- modules/simple_segmenter.py +128 -0
- modules/spellcheck_manager.py +727 -0
- modules/statuses.py +207 -0
- modules/style_guide_manager.py +315 -0
- modules/superbench_ui.py +1319 -0
- modules/superbrowser.py +329 -0
- modules/supercleaner.py +600 -0
- modules/supercleaner_ui.py +444 -0
- modules/superdocs.py +19 -0
- modules/superdocs_viewer_qt.py +382 -0
- modules/superlookup.py +252 -0
- modules/tag_cleaner.py +260 -0
- modules/tag_manager.py +351 -0
- modules/term_extractor.py +270 -0
- modules/termbase_entry_editor.py +842 -0
- modules/termbase_import_export.py +488 -0
- modules/termbase_manager.py +1060 -0
- modules/termview_widget.py +1176 -0
- modules/theme_manager.py +499 -0
- modules/tm_editor_dialog.py +99 -0
- modules/tm_manager_qt.py +1280 -0
- modules/tm_metadata_manager.py +545 -0
- modules/tmx_editor.py +1461 -0
- modules/tmx_editor_qt.py +2784 -0
- modules/tmx_generator.py +284 -0
- modules/tracked_changes.py +900 -0
- modules/trados_docx_handler.py +430 -0
- modules/translation_memory.py +715 -0
- modules/translation_results_panel.py +2134 -0
- modules/translation_services.py +282 -0
- modules/unified_prompt_library.py +659 -0
- modules/unified_prompt_manager_qt.py +3951 -0
- modules/voice_commands.py +920 -0
- modules/voice_dictation.py +477 -0
- modules/voice_dictation_lite.py +249 -0
- supervertaler-1.9.163.dist-info/METADATA +906 -0
- supervertaler-1.9.163.dist-info/RECORD +85 -0
- supervertaler-1.9.163.dist-info/WHEEL +5 -0
- supervertaler-1.9.163.dist-info/entry_points.txt +2 -0
- supervertaler-1.9.163.dist-info/licenses/LICENSE +21 -0
- supervertaler-1.9.163.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,571 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Keyboard Shortcuts Settings Widget
|
|
3
|
+
Provides UI for viewing, editing, and managing keyboard shortcuts
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from PyQt6.QtWidgets import (
|
|
8
|
+
QWidget, QVBoxLayout, QHBoxLayout, QPushButton, QTableWidget,
|
|
9
|
+
QTableWidgetItem, QHeaderView, QLineEdit, QLabel, QDialog,
|
|
10
|
+
QDialogButtonBox, QMessageBox, QFileDialog, QGroupBox, QCheckBox
|
|
11
|
+
)
|
|
12
|
+
from PyQt6.QtCore import Qt, QEvent
|
|
13
|
+
from PyQt6.QtGui import QKeySequence, QKeyEvent, QFont
|
|
14
|
+
|
|
15
|
+
from modules.shortcut_manager import ShortcutManager
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class KeySequenceEdit(QLineEdit):
|
|
19
|
+
"""Custom widget for capturing keyboard shortcuts"""
|
|
20
|
+
|
|
21
|
+
def __init__(self, parent=None):
|
|
22
|
+
super().__init__(parent)
|
|
23
|
+
self.setPlaceholderText("Press keys or click to edit...")
|
|
24
|
+
self.setReadOnly(False)
|
|
25
|
+
self.current_sequence = ""
|
|
26
|
+
|
|
27
|
+
def keyPressEvent(self, event: QKeyEvent):
|
|
28
|
+
"""Capture key press and convert to shortcut string"""
|
|
29
|
+
# Ignore modifier-only presses
|
|
30
|
+
if event.key() in (Qt.Key.Key_Control, Qt.Key.Key_Shift,
|
|
31
|
+
Qt.Key.Key_Alt, Qt.Key.Key_Meta):
|
|
32
|
+
return
|
|
33
|
+
|
|
34
|
+
# Build key sequence from modifiers + key
|
|
35
|
+
modifiers = event.modifiers()
|
|
36
|
+
key = event.key()
|
|
37
|
+
|
|
38
|
+
parts = []
|
|
39
|
+
if modifiers & Qt.KeyboardModifier.ControlModifier:
|
|
40
|
+
parts.append("Ctrl")
|
|
41
|
+
if modifiers & Qt.KeyboardModifier.AltModifier:
|
|
42
|
+
parts.append("Alt")
|
|
43
|
+
if modifiers & Qt.KeyboardModifier.ShiftModifier:
|
|
44
|
+
parts.append("Shift")
|
|
45
|
+
if modifiers & Qt.KeyboardModifier.MetaModifier:
|
|
46
|
+
parts.append("Meta")
|
|
47
|
+
|
|
48
|
+
# Get key name
|
|
49
|
+
key_name = QKeySequence(key).toString()
|
|
50
|
+
if key_name:
|
|
51
|
+
parts.append(key_name)
|
|
52
|
+
|
|
53
|
+
# Create shortcut string
|
|
54
|
+
if parts:
|
|
55
|
+
sequence = "+".join(parts)
|
|
56
|
+
self.setText(sequence)
|
|
57
|
+
self.current_sequence = sequence
|
|
58
|
+
|
|
59
|
+
event.accept()
|
|
60
|
+
|
|
61
|
+
def focusInEvent(self, event):
|
|
62
|
+
"""Clear on focus for new input"""
|
|
63
|
+
super().focusInEvent(event)
|
|
64
|
+
self.selectAll()
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class ShortcutEditDialog(QDialog):
|
|
68
|
+
"""Dialog for editing a keyboard shortcut"""
|
|
69
|
+
|
|
70
|
+
def __init__(self, shortcut_id: str, data: dict, manager: ShortcutManager, parent=None):
|
|
71
|
+
super().__init__(parent)
|
|
72
|
+
self.shortcut_id = shortcut_id
|
|
73
|
+
self.data = data
|
|
74
|
+
self.manager = manager
|
|
75
|
+
|
|
76
|
+
self.setWindowTitle(f"Edit Shortcut: {data['description']}")
|
|
77
|
+
self.setMinimumWidth(500)
|
|
78
|
+
|
|
79
|
+
layout = QVBoxLayout(self)
|
|
80
|
+
|
|
81
|
+
# Description
|
|
82
|
+
desc_label = QLabel(f"<b>Action:</b> {data['description']}")
|
|
83
|
+
desc_label.setWordWrap(True)
|
|
84
|
+
layout.addWidget(desc_label)
|
|
85
|
+
|
|
86
|
+
# Category
|
|
87
|
+
cat_label = QLabel(f"<b>Category:</b> {data['category']}")
|
|
88
|
+
layout.addWidget(cat_label)
|
|
89
|
+
|
|
90
|
+
# Default shortcut
|
|
91
|
+
default_label = QLabel(f"<b>Default:</b> {data['default']}")
|
|
92
|
+
layout.addWidget(default_label)
|
|
93
|
+
|
|
94
|
+
layout.addSpacing(10)
|
|
95
|
+
|
|
96
|
+
# Current shortcut input
|
|
97
|
+
input_layout = QHBoxLayout()
|
|
98
|
+
input_label = QLabel("New Shortcut:")
|
|
99
|
+
self.shortcut_input = KeySequenceEdit()
|
|
100
|
+
self.shortcut_input.setText(data['current'])
|
|
101
|
+
input_layout.addWidget(input_label)
|
|
102
|
+
input_layout.addWidget(self.shortcut_input)
|
|
103
|
+
layout.addLayout(input_layout)
|
|
104
|
+
|
|
105
|
+
# Reset button
|
|
106
|
+
reset_btn = QPushButton("Reset to Default")
|
|
107
|
+
reset_btn.clicked.connect(self.reset_to_default)
|
|
108
|
+
layout.addWidget(reset_btn)
|
|
109
|
+
|
|
110
|
+
# Conflict warning label
|
|
111
|
+
self.warning_label = QLabel("")
|
|
112
|
+
self.warning_label.setStyleSheet("color: #f44336; font-weight: bold;")
|
|
113
|
+
self.warning_label.setWordWrap(True)
|
|
114
|
+
layout.addWidget(self.warning_label)
|
|
115
|
+
|
|
116
|
+
layout.addSpacing(10)
|
|
117
|
+
|
|
118
|
+
# Buttons
|
|
119
|
+
buttons = QDialogButtonBox(
|
|
120
|
+
QDialogButtonBox.StandardButton.Ok |
|
|
121
|
+
QDialogButtonBox.StandardButton.Cancel
|
|
122
|
+
)
|
|
123
|
+
buttons.accepted.connect(self.accept_shortcut)
|
|
124
|
+
buttons.rejected.connect(self.reject)
|
|
125
|
+
layout.addWidget(buttons)
|
|
126
|
+
|
|
127
|
+
# Check for conflicts when text changes
|
|
128
|
+
self.shortcut_input.textChanged.connect(self.check_conflicts)
|
|
129
|
+
|
|
130
|
+
def reset_to_default(self):
|
|
131
|
+
"""Reset to default shortcut"""
|
|
132
|
+
self.shortcut_input.setText(self.data['default'])
|
|
133
|
+
|
|
134
|
+
def check_conflicts(self):
|
|
135
|
+
"""Check for conflicting shortcuts"""
|
|
136
|
+
new_sequence = self.shortcut_input.text()
|
|
137
|
+
if not new_sequence:
|
|
138
|
+
self.warning_label.setText("")
|
|
139
|
+
return
|
|
140
|
+
|
|
141
|
+
conflicts = self.manager.find_conflicts(self.shortcut_id, new_sequence)
|
|
142
|
+
if conflicts:
|
|
143
|
+
conflict_names = []
|
|
144
|
+
all_shortcuts = self.manager.get_all_shortcuts()
|
|
145
|
+
for conflict_id in conflicts:
|
|
146
|
+
conflict_names.append(all_shortcuts[conflict_id]['description'])
|
|
147
|
+
|
|
148
|
+
self.warning_label.setText(
|
|
149
|
+
f"⚠️ Warning: This shortcut conflicts with:\n" +
|
|
150
|
+
"\n".join(f" • {name}" for name in conflict_names)
|
|
151
|
+
)
|
|
152
|
+
else:
|
|
153
|
+
self.warning_label.setText("")
|
|
154
|
+
|
|
155
|
+
def accept_shortcut(self):
|
|
156
|
+
"""Accept the new shortcut"""
|
|
157
|
+
new_sequence = self.shortcut_input.text()
|
|
158
|
+
|
|
159
|
+
# Check conflicts one more time
|
|
160
|
+
conflicts = self.manager.find_conflicts(self.shortcut_id, new_sequence)
|
|
161
|
+
if conflicts:
|
|
162
|
+
reply = QMessageBox.question(
|
|
163
|
+
self,
|
|
164
|
+
"Conflict Detected",
|
|
165
|
+
"This shortcut is already in use. Do you want to override it?",
|
|
166
|
+
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
|
|
167
|
+
)
|
|
168
|
+
if reply == QMessageBox.StandardButton.No:
|
|
169
|
+
return
|
|
170
|
+
|
|
171
|
+
# Set the new shortcut
|
|
172
|
+
if new_sequence == self.data['default']:
|
|
173
|
+
# Same as default, remove custom
|
|
174
|
+
self.manager.reset_shortcut(self.shortcut_id)
|
|
175
|
+
else:
|
|
176
|
+
self.manager.set_shortcut(self.shortcut_id, new_sequence)
|
|
177
|
+
|
|
178
|
+
self.manager.save_shortcuts()
|
|
179
|
+
self.accept()
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
class KeyboardShortcutsWidget(QWidget):
|
|
183
|
+
"""Main widget for keyboard shortcuts settings"""
|
|
184
|
+
|
|
185
|
+
def __init__(self, parent=None):
|
|
186
|
+
super().__init__(parent)
|
|
187
|
+
self.main_window = parent # Store reference to main window
|
|
188
|
+
# Use main window's shortcut manager if available, otherwise create new one
|
|
189
|
+
if hasattr(parent, 'shortcut_manager'):
|
|
190
|
+
self.manager = parent.shortcut_manager
|
|
191
|
+
else:
|
|
192
|
+
self.manager = ShortcutManager()
|
|
193
|
+
self.init_ui()
|
|
194
|
+
self.load_shortcuts()
|
|
195
|
+
|
|
196
|
+
def init_ui(self):
|
|
197
|
+
"""Initialize the user interface"""
|
|
198
|
+
layout = QVBoxLayout(self)
|
|
199
|
+
layout.setContentsMargins(20, 20, 20, 20)
|
|
200
|
+
layout.setSpacing(15)
|
|
201
|
+
|
|
202
|
+
# Header
|
|
203
|
+
header = QLabel("<h2>⌨️ Keyboard Shortcuts</h2>")
|
|
204
|
+
layout.addWidget(header)
|
|
205
|
+
|
|
206
|
+
# Description
|
|
207
|
+
desc = QLabel(
|
|
208
|
+
"View and customize all keyboard shortcuts. Double-click a shortcut to edit it."
|
|
209
|
+
)
|
|
210
|
+
desc.setWordWrap(True)
|
|
211
|
+
desc.setStyleSheet("color: #666; margin-bottom: 10px;")
|
|
212
|
+
layout.addWidget(desc)
|
|
213
|
+
|
|
214
|
+
# Search/Filter
|
|
215
|
+
search_layout = QHBoxLayout()
|
|
216
|
+
search_label = QLabel("Search:")
|
|
217
|
+
self.search_input = QLineEdit()
|
|
218
|
+
self.search_input.setPlaceholderText("Filter by action or shortcut...")
|
|
219
|
+
self.search_input.textChanged.connect(self.filter_shortcuts)
|
|
220
|
+
search_layout.addWidget(search_label)
|
|
221
|
+
search_layout.addWidget(self.search_input)
|
|
222
|
+
layout.addLayout(search_layout)
|
|
223
|
+
|
|
224
|
+
# Shortcuts table
|
|
225
|
+
self.table = QTableWidget()
|
|
226
|
+
self.table.setColumnCount(5)
|
|
227
|
+
self.table.setHorizontalHeaderLabels(["Enabled", "Category", "Action", "Shortcut", "Status"])
|
|
228
|
+
self.table.horizontalHeader().setSectionResizeMode(0, QHeaderView.ResizeMode.ResizeToContents)
|
|
229
|
+
self.table.horizontalHeader().setSectionResizeMode(1, QHeaderView.ResizeMode.ResizeToContents)
|
|
230
|
+
self.table.horizontalHeader().setSectionResizeMode(2, QHeaderView.ResizeMode.Stretch)
|
|
231
|
+
self.table.horizontalHeader().setSectionResizeMode(3, QHeaderView.ResizeMode.ResizeToContents)
|
|
232
|
+
self.table.horizontalHeader().setSectionResizeMode(4, QHeaderView.ResizeMode.ResizeToContents)
|
|
233
|
+
self.table.setAlternatingRowColors(True)
|
|
234
|
+
self.table.setSelectionBehavior(QTableWidget.SelectionBehavior.SelectRows)
|
|
235
|
+
self.table.setSelectionMode(QTableWidget.SelectionMode.SingleSelection)
|
|
236
|
+
self.table.setEditTriggers(QTableWidget.EditTrigger.NoEditTriggers)
|
|
237
|
+
self.table.setSortingEnabled(True) # Enable column sorting
|
|
238
|
+
self.table.doubleClicked.connect(self.edit_selected_shortcut)
|
|
239
|
+
|
|
240
|
+
# Style the table
|
|
241
|
+
self.table.setStyleSheet("""
|
|
242
|
+
QTableWidget {
|
|
243
|
+
border: 1px solid #ddd;
|
|
244
|
+
gridline-color: #f0f0f0;
|
|
245
|
+
}
|
|
246
|
+
QTableWidget::item {
|
|
247
|
+
padding: 5px;
|
|
248
|
+
}
|
|
249
|
+
QTableWidget::item:selected {
|
|
250
|
+
background-color: #2196F3;
|
|
251
|
+
color: white;
|
|
252
|
+
}
|
|
253
|
+
""")
|
|
254
|
+
|
|
255
|
+
layout.addWidget(self.table)
|
|
256
|
+
|
|
257
|
+
# Action buttons
|
|
258
|
+
button_layout = QHBoxLayout()
|
|
259
|
+
|
|
260
|
+
edit_btn = QPushButton("✏️ Edit Selected")
|
|
261
|
+
edit_btn.clicked.connect(self.edit_selected_shortcut)
|
|
262
|
+
button_layout.addWidget(edit_btn)
|
|
263
|
+
|
|
264
|
+
reset_btn = QPushButton("🔄 Reset Selected to Default")
|
|
265
|
+
reset_btn.clicked.connect(self.reset_selected)
|
|
266
|
+
button_layout.addWidget(reset_btn)
|
|
267
|
+
|
|
268
|
+
reset_all_btn = QPushButton("🔄 Reset All to Defaults")
|
|
269
|
+
reset_all_btn.clicked.connect(self.reset_all)
|
|
270
|
+
button_layout.addWidget(reset_all_btn)
|
|
271
|
+
|
|
272
|
+
button_layout.addStretch()
|
|
273
|
+
layout.addLayout(button_layout)
|
|
274
|
+
|
|
275
|
+
# Export/Import buttons
|
|
276
|
+
io_group = QGroupBox("Import/Export")
|
|
277
|
+
io_layout = QHBoxLayout(io_group)
|
|
278
|
+
|
|
279
|
+
export_json_btn = QPushButton("📤 Export Shortcuts (JSON)")
|
|
280
|
+
export_json_btn.clicked.connect(self.export_shortcuts)
|
|
281
|
+
io_layout.addWidget(export_json_btn)
|
|
282
|
+
|
|
283
|
+
import_json_btn = QPushButton("📥 Import Shortcuts (JSON)")
|
|
284
|
+
import_json_btn.clicked.connect(self.import_shortcuts)
|
|
285
|
+
io_layout.addWidget(import_json_btn)
|
|
286
|
+
|
|
287
|
+
export_html_btn = QPushButton("📄 Export Cheatsheet (HTML)")
|
|
288
|
+
export_html_btn.clicked.connect(self.export_html_cheatsheet)
|
|
289
|
+
export_html_btn.setStyleSheet("background-color: #4CAF50; color: white; font-weight: bold;")
|
|
290
|
+
io_layout.addWidget(export_html_btn)
|
|
291
|
+
|
|
292
|
+
layout.addWidget(io_group)
|
|
293
|
+
|
|
294
|
+
# Info label
|
|
295
|
+
info = QLabel(
|
|
296
|
+
"💡 Tip: Exported HTML cheatsheets can be printed or saved as PDF for reference."
|
|
297
|
+
)
|
|
298
|
+
info.setWordWrap(True)
|
|
299
|
+
info.setStyleSheet("color: #666; font-style: italic; margin-top: 5px;")
|
|
300
|
+
layout.addWidget(info)
|
|
301
|
+
|
|
302
|
+
def load_shortcuts(self):
|
|
303
|
+
"""Load shortcuts into the table"""
|
|
304
|
+
self.table.setRowCount(0)
|
|
305
|
+
|
|
306
|
+
all_shortcuts = self.manager.get_all_shortcuts()
|
|
307
|
+
shortcuts_by_category = self.manager.get_shortcuts_by_category()
|
|
308
|
+
|
|
309
|
+
row = 0
|
|
310
|
+
for category in sorted(shortcuts_by_category.keys()):
|
|
311
|
+
shortcuts = shortcuts_by_category[category]
|
|
312
|
+
|
|
313
|
+
for shortcut_id, data in sorted(shortcuts, key=lambda x: x[1]["description"]):
|
|
314
|
+
self.table.insertRow(row)
|
|
315
|
+
|
|
316
|
+
# Enabled checkbox (column 0)
|
|
317
|
+
checkbox = QCheckBox()
|
|
318
|
+
checkbox.setChecked(data.get("is_enabled", True))
|
|
319
|
+
checkbox.setStyleSheet("margin-left: 10px;")
|
|
320
|
+
checkbox.setToolTip("Enable or disable this shortcut")
|
|
321
|
+
# Store shortcut_id in checkbox for reference
|
|
322
|
+
checkbox.setProperty("shortcut_id", shortcut_id)
|
|
323
|
+
checkbox.stateChanged.connect(self._on_enabled_changed)
|
|
324
|
+
# Create a widget container to center the checkbox
|
|
325
|
+
checkbox_container = QWidget()
|
|
326
|
+
checkbox_layout = QHBoxLayout(checkbox_container)
|
|
327
|
+
checkbox_layout.setAlignment(Qt.AlignmentFlag.AlignCenter)
|
|
328
|
+
checkbox_layout.setContentsMargins(0, 0, 0, 0)
|
|
329
|
+
checkbox_layout.addWidget(checkbox)
|
|
330
|
+
self.table.setCellWidget(row, 0, checkbox_container)
|
|
331
|
+
|
|
332
|
+
# Category (column 1)
|
|
333
|
+
cat_item = QTableWidgetItem(data["category"])
|
|
334
|
+
cat_item.setData(Qt.ItemDataRole.UserRole, shortcut_id) # Store ID
|
|
335
|
+
self.table.setItem(row, 1, cat_item)
|
|
336
|
+
|
|
337
|
+
# Action (column 2)
|
|
338
|
+
action_item = QTableWidgetItem(data["description"])
|
|
339
|
+
self.table.setItem(row, 2, action_item)
|
|
340
|
+
|
|
341
|
+
# Shortcut (column 3)
|
|
342
|
+
shortcut_item = QTableWidgetItem(data["current"])
|
|
343
|
+
shortcut_font = QFont()
|
|
344
|
+
shortcut_font.setFamily("Courier New")
|
|
345
|
+
shortcut_font.setBold(True)
|
|
346
|
+
shortcut_item.setFont(shortcut_font)
|
|
347
|
+
# Gray out if disabled
|
|
348
|
+
if not data.get("is_enabled", True):
|
|
349
|
+
shortcut_item.setForeground(Qt.GlobalColor.gray)
|
|
350
|
+
else:
|
|
351
|
+
shortcut_item.setForeground(Qt.GlobalColor.blue)
|
|
352
|
+
self.table.setItem(row, 3, shortcut_item)
|
|
353
|
+
|
|
354
|
+
# Status (column 4)
|
|
355
|
+
status = "Custom" if data["is_custom"] else "Default"
|
|
356
|
+
status_item = QTableWidgetItem(status)
|
|
357
|
+
if data["is_custom"]:
|
|
358
|
+
status_item.setForeground(Qt.GlobalColor.darkGreen)
|
|
359
|
+
status_font = QFont()
|
|
360
|
+
status_font.setBold(True)
|
|
361
|
+
status_item.setFont(status_font)
|
|
362
|
+
self.table.setItem(row, 4, status_item)
|
|
363
|
+
|
|
364
|
+
row += 1
|
|
365
|
+
|
|
366
|
+
def _on_enabled_changed(self, state):
|
|
367
|
+
"""Handle checkbox state change for enabling/disabling shortcuts"""
|
|
368
|
+
checkbox = self.sender()
|
|
369
|
+
if checkbox:
|
|
370
|
+
shortcut_id = checkbox.property("shortcut_id")
|
|
371
|
+
if shortcut_id:
|
|
372
|
+
is_enabled = state == Qt.CheckState.Checked.value
|
|
373
|
+
if is_enabled:
|
|
374
|
+
self.manager.enable_shortcut(shortcut_id)
|
|
375
|
+
else:
|
|
376
|
+
self.manager.disable_shortcut(shortcut_id)
|
|
377
|
+
self.manager.save_shortcuts()
|
|
378
|
+
# Update the shortcut text color to indicate disabled state
|
|
379
|
+
self._update_shortcut_text_color(shortcut_id, is_enabled)
|
|
380
|
+
# Immediately refresh the actual shortcut enabled states in the main window
|
|
381
|
+
if self.main_window and hasattr(self.main_window, 'refresh_shortcut_enabled_states'):
|
|
382
|
+
self.main_window.refresh_shortcut_enabled_states()
|
|
383
|
+
|
|
384
|
+
def _update_shortcut_text_color(self, shortcut_id: str, is_enabled: bool):
|
|
385
|
+
"""Update the shortcut text color based on enabled state"""
|
|
386
|
+
for row in range(self.table.rowCount()):
|
|
387
|
+
cat_item = self.table.item(row, 1)
|
|
388
|
+
if cat_item and cat_item.data(Qt.ItemDataRole.UserRole) == shortcut_id:
|
|
389
|
+
shortcut_item = self.table.item(row, 3)
|
|
390
|
+
if shortcut_item:
|
|
391
|
+
if is_enabled:
|
|
392
|
+
shortcut_item.setForeground(Qt.GlobalColor.blue)
|
|
393
|
+
else:
|
|
394
|
+
shortcut_item.setForeground(Qt.GlobalColor.gray)
|
|
395
|
+
break
|
|
396
|
+
|
|
397
|
+
def filter_shortcuts(self):
|
|
398
|
+
"""Filter shortcuts based on search text"""
|
|
399
|
+
search_text = self.search_input.text().lower()
|
|
400
|
+
|
|
401
|
+
for row in range(self.table.rowCount()):
|
|
402
|
+
action = self.table.item(row, 2).text().lower()
|
|
403
|
+
shortcut = self.table.item(row, 3).text().lower()
|
|
404
|
+
category = self.table.item(row, 1).text().lower()
|
|
405
|
+
|
|
406
|
+
if search_text in action or search_text in shortcut or search_text in category:
|
|
407
|
+
self.table.setRowHidden(row, False)
|
|
408
|
+
else:
|
|
409
|
+
self.table.setRowHidden(row, True)
|
|
410
|
+
|
|
411
|
+
def edit_selected_shortcut(self):
|
|
412
|
+
"""Edit the selected shortcut"""
|
|
413
|
+
current_row = self.table.currentRow()
|
|
414
|
+
if current_row < 0:
|
|
415
|
+
QMessageBox.information(self, "No Selection", "Please select a shortcut to edit.")
|
|
416
|
+
return
|
|
417
|
+
|
|
418
|
+
# Get shortcut ID from Category column (column 1)
|
|
419
|
+
shortcut_id = self.table.item(current_row, 1).data(Qt.ItemDataRole.UserRole)
|
|
420
|
+
all_shortcuts = self.manager.get_all_shortcuts()
|
|
421
|
+
data = all_shortcuts[shortcut_id]
|
|
422
|
+
|
|
423
|
+
# Open edit dialog
|
|
424
|
+
dialog = ShortcutEditDialog(shortcut_id, data, self.manager, self)
|
|
425
|
+
if dialog.exec() == QDialog.DialogCode.Accepted:
|
|
426
|
+
self.load_shortcuts() # Reload to show changes
|
|
427
|
+
QMessageBox.information(
|
|
428
|
+
self,
|
|
429
|
+
"Shortcut Updated",
|
|
430
|
+
"The shortcut has been updated. Changes will take effect when you restart the application."
|
|
431
|
+
)
|
|
432
|
+
|
|
433
|
+
def reset_selected(self):
|
|
434
|
+
"""Reset selected shortcut to default"""
|
|
435
|
+
current_row = self.table.currentRow()
|
|
436
|
+
if current_row < 0:
|
|
437
|
+
QMessageBox.information(self, "No Selection", "Please select a shortcut to reset.")
|
|
438
|
+
return
|
|
439
|
+
|
|
440
|
+
shortcut_id = self.table.item(current_row, 1).data(Qt.ItemDataRole.UserRole)
|
|
441
|
+
all_shortcuts = self.manager.get_all_shortcuts()
|
|
442
|
+
data = all_shortcuts[shortcut_id]
|
|
443
|
+
|
|
444
|
+
if not data["is_custom"]:
|
|
445
|
+
QMessageBox.information(self, "Already Default", "This shortcut is already using its default value.")
|
|
446
|
+
return
|
|
447
|
+
|
|
448
|
+
reply = QMessageBox.question(
|
|
449
|
+
self,
|
|
450
|
+
"Reset Shortcut",
|
|
451
|
+
f"Reset '{data['description']}' to its default shortcut ({data['default']})?",
|
|
452
|
+
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
|
|
453
|
+
)
|
|
454
|
+
|
|
455
|
+
if reply == QMessageBox.StandardButton.Yes:
|
|
456
|
+
self.manager.reset_shortcut(shortcut_id)
|
|
457
|
+
self.manager.save_shortcuts()
|
|
458
|
+
self.load_shortcuts()
|
|
459
|
+
QMessageBox.information(self, "Reset Complete", "Shortcut has been reset to default.")
|
|
460
|
+
|
|
461
|
+
def reset_all(self):
|
|
462
|
+
"""Reset all shortcuts to defaults"""
|
|
463
|
+
reply = QMessageBox.question(
|
|
464
|
+
self,
|
|
465
|
+
"Reset All Shortcuts",
|
|
466
|
+
"Are you sure you want to reset ALL shortcuts to their default values?\n\nThis cannot be undone.",
|
|
467
|
+
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
|
|
468
|
+
QMessageBox.StandardButton.No
|
|
469
|
+
)
|
|
470
|
+
|
|
471
|
+
if reply == QMessageBox.StandardButton.Yes:
|
|
472
|
+
self.manager.reset_all_shortcuts()
|
|
473
|
+
self.manager.save_shortcuts()
|
|
474
|
+
self.load_shortcuts()
|
|
475
|
+
QMessageBox.information(self, "Reset Complete", "All shortcuts have been reset to defaults.")
|
|
476
|
+
|
|
477
|
+
def export_shortcuts(self):
|
|
478
|
+
"""Export shortcuts to JSON file"""
|
|
479
|
+
file_path, _ = QFileDialog.getSaveFileName(
|
|
480
|
+
self,
|
|
481
|
+
"Export Shortcuts",
|
|
482
|
+
"supervertaler_shortcuts.json",
|
|
483
|
+
"JSON Files (*.json)"
|
|
484
|
+
)
|
|
485
|
+
|
|
486
|
+
if file_path:
|
|
487
|
+
try:
|
|
488
|
+
self.manager.export_shortcuts(Path(file_path))
|
|
489
|
+
QMessageBox.information(
|
|
490
|
+
self,
|
|
491
|
+
"Export Successful",
|
|
492
|
+
f"Shortcuts exported to:\n{file_path}"
|
|
493
|
+
)
|
|
494
|
+
except Exception as e:
|
|
495
|
+
QMessageBox.critical(
|
|
496
|
+
self,
|
|
497
|
+
"Export Failed",
|
|
498
|
+
f"Failed to export shortcuts:\n{str(e)}"
|
|
499
|
+
)
|
|
500
|
+
|
|
501
|
+
def import_shortcuts(self):
|
|
502
|
+
"""Import shortcuts from JSON file"""
|
|
503
|
+
file_path, _ = QFileDialog.getOpenFileName(
|
|
504
|
+
self,
|
|
505
|
+
"Import Shortcuts",
|
|
506
|
+
"",
|
|
507
|
+
"JSON Files (*.json)"
|
|
508
|
+
)
|
|
509
|
+
|
|
510
|
+
if file_path:
|
|
511
|
+
reply = QMessageBox.question(
|
|
512
|
+
self,
|
|
513
|
+
"Import Shortcuts",
|
|
514
|
+
"This will replace your current custom shortcuts. Continue?",
|
|
515
|
+
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
|
|
516
|
+
)
|
|
517
|
+
|
|
518
|
+
if reply == QMessageBox.StandardButton.Yes:
|
|
519
|
+
try:
|
|
520
|
+
if self.manager.import_shortcuts(Path(file_path)):
|
|
521
|
+
self.manager.save_shortcuts()
|
|
522
|
+
self.load_shortcuts()
|
|
523
|
+
QMessageBox.information(
|
|
524
|
+
self,
|
|
525
|
+
"Import Successful",
|
|
526
|
+
"Shortcuts imported successfully.\n\nChanges will take effect when you restart the application."
|
|
527
|
+
)
|
|
528
|
+
else:
|
|
529
|
+
QMessageBox.critical(
|
|
530
|
+
self,
|
|
531
|
+
"Import Failed",
|
|
532
|
+
"Invalid shortcuts file format."
|
|
533
|
+
)
|
|
534
|
+
except Exception as e:
|
|
535
|
+
QMessageBox.critical(
|
|
536
|
+
self,
|
|
537
|
+
"Import Failed",
|
|
538
|
+
f"Failed to import shortcuts:\n{str(e)}"
|
|
539
|
+
)
|
|
540
|
+
|
|
541
|
+
def export_html_cheatsheet(self):
|
|
542
|
+
"""Export shortcuts as HTML cheatsheet"""
|
|
543
|
+
file_path, _ = QFileDialog.getSaveFileName(
|
|
544
|
+
self,
|
|
545
|
+
"Export HTML Cheatsheet",
|
|
546
|
+
"supervertaler_shortcuts.html",
|
|
547
|
+
"HTML Files (*.html)"
|
|
548
|
+
)
|
|
549
|
+
|
|
550
|
+
if file_path:
|
|
551
|
+
try:
|
|
552
|
+
self.manager.export_html_cheatsheet(Path(file_path))
|
|
553
|
+
|
|
554
|
+
reply = QMessageBox.question(
|
|
555
|
+
self,
|
|
556
|
+
"Export Successful",
|
|
557
|
+
f"HTML cheatsheet exported to:\n{file_path}\n\nWould you like to open it in your browser?",
|
|
558
|
+
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
|
|
559
|
+
)
|
|
560
|
+
|
|
561
|
+
if reply == QMessageBox.StandardButton.Yes:
|
|
562
|
+
import webbrowser
|
|
563
|
+
webbrowser.open(file_path)
|
|
564
|
+
|
|
565
|
+
except Exception as e:
|
|
566
|
+
QMessageBox.critical(
|
|
567
|
+
self,
|
|
568
|
+
"Export Failed",
|
|
569
|
+
f"Failed to export cheatsheet:\n{str(e)}"
|
|
570
|
+
)
|
|
571
|
+
|