supervertaler 1.9.153__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.

Potentially problematic release.


This version of supervertaler might be problematic. Click here for more details.

Files changed (85) hide show
  1. Supervertaler.py +47886 -0
  2. modules/__init__.py +10 -0
  3. modules/ai_actions.py +964 -0
  4. modules/ai_attachment_manager.py +343 -0
  5. modules/ai_file_viewer_dialog.py +210 -0
  6. modules/autofingers_engine.py +466 -0
  7. modules/cafetran_docx_handler.py +379 -0
  8. modules/config_manager.py +469 -0
  9. modules/database_manager.py +1878 -0
  10. modules/database_migrations.py +417 -0
  11. modules/dejavurtf_handler.py +779 -0
  12. modules/document_analyzer.py +427 -0
  13. modules/docx_handler.py +689 -0
  14. modules/encoding_repair.py +319 -0
  15. modules/encoding_repair_Qt.py +393 -0
  16. modules/encoding_repair_ui.py +481 -0
  17. modules/feature_manager.py +350 -0
  18. modules/figure_context_manager.py +340 -0
  19. modules/file_dialog_helper.py +148 -0
  20. modules/find_replace.py +164 -0
  21. modules/find_replace_qt.py +457 -0
  22. modules/glossary_manager.py +433 -0
  23. modules/image_extractor.py +188 -0
  24. modules/keyboard_shortcuts_widget.py +571 -0
  25. modules/llm_clients.py +1211 -0
  26. modules/llm_leaderboard.py +737 -0
  27. modules/llm_superbench_ui.py +1401 -0
  28. modules/local_llm_setup.py +1104 -0
  29. modules/model_update_dialog.py +381 -0
  30. modules/model_version_checker.py +373 -0
  31. modules/mqxliff_handler.py +638 -0
  32. modules/non_translatables_manager.py +743 -0
  33. modules/pdf_rescue_Qt.py +1822 -0
  34. modules/pdf_rescue_tkinter.py +909 -0
  35. modules/phrase_docx_handler.py +516 -0
  36. modules/project_home_panel.py +209 -0
  37. modules/prompt_assistant.py +357 -0
  38. modules/prompt_library.py +689 -0
  39. modules/prompt_library_migration.py +447 -0
  40. modules/quick_access_sidebar.py +282 -0
  41. modules/ribbon_widget.py +597 -0
  42. modules/sdlppx_handler.py +874 -0
  43. modules/setup_wizard.py +353 -0
  44. modules/shortcut_manager.py +932 -0
  45. modules/simple_segmenter.py +128 -0
  46. modules/spellcheck_manager.py +727 -0
  47. modules/statuses.py +207 -0
  48. modules/style_guide_manager.py +315 -0
  49. modules/superbench_ui.py +1319 -0
  50. modules/superbrowser.py +329 -0
  51. modules/supercleaner.py +600 -0
  52. modules/supercleaner_ui.py +444 -0
  53. modules/superdocs.py +19 -0
  54. modules/superdocs_viewer_qt.py +382 -0
  55. modules/superlookup.py +252 -0
  56. modules/tag_cleaner.py +260 -0
  57. modules/tag_manager.py +333 -0
  58. modules/term_extractor.py +270 -0
  59. modules/termbase_entry_editor.py +842 -0
  60. modules/termbase_import_export.py +488 -0
  61. modules/termbase_manager.py +1060 -0
  62. modules/termview_widget.py +1172 -0
  63. modules/theme_manager.py +499 -0
  64. modules/tm_editor_dialog.py +99 -0
  65. modules/tm_manager_qt.py +1280 -0
  66. modules/tm_metadata_manager.py +545 -0
  67. modules/tmx_editor.py +1461 -0
  68. modules/tmx_editor_qt.py +2784 -0
  69. modules/tmx_generator.py +284 -0
  70. modules/tracked_changes.py +900 -0
  71. modules/trados_docx_handler.py +430 -0
  72. modules/translation_memory.py +715 -0
  73. modules/translation_results_panel.py +2134 -0
  74. modules/translation_services.py +282 -0
  75. modules/unified_prompt_library.py +659 -0
  76. modules/unified_prompt_manager_qt.py +3951 -0
  77. modules/voice_commands.py +920 -0
  78. modules/voice_dictation.py +477 -0
  79. modules/voice_dictation_lite.py +249 -0
  80. supervertaler-1.9.153.dist-info/METADATA +896 -0
  81. supervertaler-1.9.153.dist-info/RECORD +85 -0
  82. supervertaler-1.9.153.dist-info/WHEEL +5 -0
  83. supervertaler-1.9.153.dist-info/entry_points.txt +2 -0
  84. supervertaler-1.9.153.dist-info/licenses/LICENSE +21 -0
  85. supervertaler-1.9.153.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
+