supervertaler 1.9.185__py3-none-any.whl → 1.9.196__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.
- Supervertaler.py +1123 -276
- modules/keyboard_shortcuts_widget.py +76 -8
- modules/llm_clients.py +58 -33
- modules/quicktrans.py +670 -0
- modules/shortcut_manager.py +19 -5
- modules/statuses.py +2 -2
- modules/superbrowser.py +22 -0
- modules/superlookup.py +3 -3
- modules/unified_prompt_manager_qt.py +22 -1
- {supervertaler-1.9.185.dist-info → supervertaler-1.9.196.dist-info}/METADATA +1 -1
- {supervertaler-1.9.185.dist-info → supervertaler-1.9.196.dist-info}/RECORD +15 -14
- {supervertaler-1.9.185.dist-info → supervertaler-1.9.196.dist-info}/WHEEL +0 -0
- {supervertaler-1.9.185.dist-info → supervertaler-1.9.196.dist-info}/entry_points.txt +0 -0
- {supervertaler-1.9.185.dist-info → supervertaler-1.9.196.dist-info}/licenses/LICENSE +0 -0
- {supervertaler-1.9.185.dist-info → supervertaler-1.9.196.dist-info}/top_level.txt +0 -0
Supervertaler.py
CHANGED
|
@@ -32,9 +32,9 @@ License: MIT
|
|
|
32
32
|
"""
|
|
33
33
|
|
|
34
34
|
# Version Information.
|
|
35
|
-
__version__ = "1.9.
|
|
35
|
+
__version__ = "1.9.196"
|
|
36
36
|
__phase__ = "0.9"
|
|
37
|
-
__release_date__ = "2026-02-
|
|
37
|
+
__release_date__ = "2026-02-02"
|
|
38
38
|
__edition__ = "Qt"
|
|
39
39
|
|
|
40
40
|
import sys
|
|
@@ -2298,10 +2298,15 @@ class ReadOnlyGridTextEditor(QTextEdit):
|
|
|
2298
2298
|
|
|
2299
2299
|
# Superlookup search action
|
|
2300
2300
|
if self.textCursor().hasSelection():
|
|
2301
|
-
superlookup_action = QAction("🔍 Search in
|
|
2301
|
+
superlookup_action = QAction("🔍 Search in SuperLookup (Ctrl+K)", self)
|
|
2302
2302
|
superlookup_action.triggered.connect(self._handle_superlookup_search)
|
|
2303
2303
|
menu.addAction(superlookup_action)
|
|
2304
|
-
|
|
2304
|
+
|
|
2305
|
+
# MT Quick Lookup action
|
|
2306
|
+
mt_lookup_action = QAction("⚡ QuickTrans (Ctrl+M)", self)
|
|
2307
|
+
mt_lookup_action.triggered.connect(self._handle_mt_quick_lookup)
|
|
2308
|
+
menu.addAction(mt_lookup_action)
|
|
2309
|
+
menu.addSeparator()
|
|
2305
2310
|
|
|
2306
2311
|
# QuickMenu (prompt-based actions)
|
|
2307
2312
|
try:
|
|
@@ -2355,7 +2360,7 @@ class ReadOnlyGridTextEditor(QTextEdit):
|
|
|
2355
2360
|
"""Handle Ctrl+Alt+N: Add selected text to active non-translatable list(s)"""
|
|
2356
2361
|
# Get selected text
|
|
2357
2362
|
selected_text = self.textCursor().selectedText().strip()
|
|
2358
|
-
|
|
2363
|
+
|
|
2359
2364
|
if not selected_text:
|
|
2360
2365
|
from PyQt6.QtWidgets import QMessageBox
|
|
2361
2366
|
QMessageBox.warning(
|
|
@@ -2364,14 +2369,14 @@ class ReadOnlyGridTextEditor(QTextEdit):
|
|
|
2364
2369
|
"Please select text in the Source cell before adding to non-translatables."
|
|
2365
2370
|
)
|
|
2366
2371
|
return
|
|
2367
|
-
|
|
2372
|
+
|
|
2368
2373
|
# Find main window and call add_to_nt method
|
|
2369
2374
|
table = self.table_ref if hasattr(self, 'table_ref') else self.parent()
|
|
2370
2375
|
if table:
|
|
2371
2376
|
main_window = table.parent()
|
|
2372
2377
|
while main_window and not hasattr(main_window, 'add_text_to_non_translatables'):
|
|
2373
2378
|
main_window = main_window.parent()
|
|
2374
|
-
|
|
2379
|
+
|
|
2375
2380
|
if main_window and hasattr(main_window, 'add_text_to_non_translatables'):
|
|
2376
2381
|
main_window.add_text_to_non_translatables(selected_text)
|
|
2377
2382
|
else:
|
|
@@ -2382,6 +2387,16 @@ class ReadOnlyGridTextEditor(QTextEdit):
|
|
|
2382
2387
|
"Non-translatables functionality not available."
|
|
2383
2388
|
)
|
|
2384
2389
|
|
|
2390
|
+
def _handle_mt_quick_lookup(self):
|
|
2391
|
+
"""Handle right-click: Open MT Quick Lookup popup for selected text or full source"""
|
|
2392
|
+
# Get selected text (if any)
|
|
2393
|
+
selected_text = self.textCursor().selectedText().strip() if self.textCursor().hasSelection() else None
|
|
2394
|
+
|
|
2395
|
+
# Find main window and call show_mt_quick_popup
|
|
2396
|
+
main_window = self._get_main_window()
|
|
2397
|
+
if main_window and hasattr(main_window, 'show_mt_quick_popup'):
|
|
2398
|
+
main_window.show_mt_quick_popup(text_override=selected_text)
|
|
2399
|
+
|
|
2385
2400
|
def set_background_color(self, color: str):
|
|
2386
2401
|
"""Set the background color for this text editor (for alternating row colors)"""
|
|
2387
2402
|
self.setStyleSheet(f"""
|
|
@@ -2991,10 +3006,15 @@ class EditableGridTextEditor(QTextEdit):
|
|
|
2991
3006
|
|
|
2992
3007
|
# Superlookup search action
|
|
2993
3008
|
if self.textCursor().hasSelection():
|
|
2994
|
-
superlookup_action = QAction("🔍 Search in
|
|
3009
|
+
superlookup_action = QAction("🔍 Search in SuperLookup (Ctrl+K)", self)
|
|
2995
3010
|
superlookup_action.triggered.connect(self._handle_superlookup_search)
|
|
2996
3011
|
menu.addAction(superlookup_action)
|
|
2997
|
-
|
|
3012
|
+
|
|
3013
|
+
# MT Quick Lookup action
|
|
3014
|
+
mt_lookup_action = QAction("⚡ QuickTrans (Ctrl+M)", self)
|
|
3015
|
+
mt_lookup_action.triggered.connect(self._handle_mt_quick_lookup)
|
|
3016
|
+
menu.addAction(mt_lookup_action)
|
|
3017
|
+
menu.addSeparator()
|
|
2998
3018
|
|
|
2999
3019
|
# QuickMenu (prompt-based actions)
|
|
3000
3020
|
try:
|
|
@@ -3507,6 +3527,24 @@ class EditableGridTextEditor(QTextEdit):
|
|
|
3507
3527
|
"Non-translatables functionality not available."
|
|
3508
3528
|
)
|
|
3509
3529
|
|
|
3530
|
+
def _handle_mt_quick_lookup(self):
|
|
3531
|
+
"""Handle right-click: Open MT Quick Lookup popup for selected text or full source"""
|
|
3532
|
+
# Get selected text (if any) - prefer from target, then try source
|
|
3533
|
+
selected_text = self.textCursor().selectedText().strip() if self.textCursor().hasSelection() else None
|
|
3534
|
+
|
|
3535
|
+
if not selected_text and self.table and self.row >= 0:
|
|
3536
|
+
# Try getting selected text from source cell
|
|
3537
|
+
source_widget = self.table.cellWidget(self.row, 2)
|
|
3538
|
+
if source_widget and hasattr(source_widget, 'textCursor'):
|
|
3539
|
+
cursor = source_widget.textCursor()
|
|
3540
|
+
if cursor.hasSelection():
|
|
3541
|
+
selected_text = cursor.selectedText().strip()
|
|
3542
|
+
|
|
3543
|
+
# Find main window and call show_mt_quick_popup
|
|
3544
|
+
main_window = self._get_main_window()
|
|
3545
|
+
if main_window and hasattr(main_window, 'show_mt_quick_popup'):
|
|
3546
|
+
main_window.show_mt_quick_popup(text_override=selected_text)
|
|
3547
|
+
|
|
3510
3548
|
def _insert_next_tag_or_wrap_selection(self):
|
|
3511
3549
|
"""
|
|
3512
3550
|
Insert the next memoQ tag, HTML tag, or CafeTran pipe symbol from source, or wrap selection.
|
|
@@ -6435,7 +6473,7 @@ class SupervertalerQt(QMainWindow):
|
|
|
6435
6473
|
|
|
6436
6474
|
# Restore Termview under grid visibility state
|
|
6437
6475
|
if hasattr(self, 'bottom_tabs'):
|
|
6438
|
-
termview_visible = general_settings.get('termview_under_grid_visible',
|
|
6476
|
+
termview_visible = general_settings.get('termview_under_grid_visible', False)
|
|
6439
6477
|
self.bottom_tabs.setVisible(termview_visible)
|
|
6440
6478
|
if hasattr(self, 'termview_visible_action'):
|
|
6441
6479
|
self.termview_visible_action.setChecked(termview_visible)
|
|
@@ -7283,7 +7321,12 @@ class SupervertalerQt(QMainWindow):
|
|
|
7283
7321
|
|
|
7284
7322
|
# Alt+K - Open QuickMenu directly
|
|
7285
7323
|
create_shortcut("editor_open_quickmenu", "Alt+K", self.open_quickmenu)
|
|
7286
|
-
|
|
7324
|
+
|
|
7325
|
+
# Ctrl+Shift+Q - MT Quick Lookup (GT4T-style popup)
|
|
7326
|
+
mt_quick_shortcut = create_shortcut("mt_quick_lookup", "Ctrl+Shift+Q", self.show_mt_quick_popup)
|
|
7327
|
+
# Use ApplicationShortcut context so it works even when focus is in QTextEdit widgets
|
|
7328
|
+
mt_quick_shortcut.setContext(Qt.ShortcutContext.ApplicationShortcut)
|
|
7329
|
+
|
|
7287
7330
|
def focus_segment_notes(self):
|
|
7288
7331
|
"""Switch to Segment Note tab and focus the notes editor so user can start typing immediately"""
|
|
7289
7332
|
if not hasattr(self, 'right_tabs'):
|
|
@@ -7355,7 +7398,105 @@ class SupervertalerQt(QMainWindow):
|
|
|
7355
7398
|
|
|
7356
7399
|
except Exception as e:
|
|
7357
7400
|
self.log(f"❌ Error opening QuickMenu: {e}")
|
|
7358
|
-
|
|
7401
|
+
|
|
7402
|
+
def show_mt_quick_popup(self, text_override: str = None):
|
|
7403
|
+
"""Show GT4T-style MT Quick Lookup popup with translations from all enabled MT engines.
|
|
7404
|
+
|
|
7405
|
+
Triggered by Ctrl+Shift+Q or right-click menu. Shows machine translation
|
|
7406
|
+
suggestions from all configured and enabled MT providers in a popup window.
|
|
7407
|
+
|
|
7408
|
+
Args:
|
|
7409
|
+
text_override: Optional text to translate. If None, uses selected text
|
|
7410
|
+
in the current cell, or falls back to the full source text.
|
|
7411
|
+
|
|
7412
|
+
Features:
|
|
7413
|
+
- Displays source text at top
|
|
7414
|
+
- Shows numbered list of MT suggestions from each provider
|
|
7415
|
+
- Press 1-9 to quickly insert a translation
|
|
7416
|
+
- Arrow keys to navigate, Enter to insert selected
|
|
7417
|
+
- Escape to dismiss
|
|
7418
|
+
"""
|
|
7419
|
+
try:
|
|
7420
|
+
# Get current segment
|
|
7421
|
+
current_row = self.table.currentRow()
|
|
7422
|
+
if current_row < 0:
|
|
7423
|
+
self.log("⚠️ No segment selected")
|
|
7424
|
+
return
|
|
7425
|
+
|
|
7426
|
+
# Determine what text to translate
|
|
7427
|
+
text_to_translate = text_override
|
|
7428
|
+
|
|
7429
|
+
if not text_to_translate:
|
|
7430
|
+
# Check for selected text in the currently focused widget
|
|
7431
|
+
focus_widget = QApplication.focusWidget()
|
|
7432
|
+
if focus_widget and hasattr(focus_widget, 'textCursor'):
|
|
7433
|
+
cursor = focus_widget.textCursor()
|
|
7434
|
+
if cursor.hasSelection():
|
|
7435
|
+
text_to_translate = cursor.selectedText().strip()
|
|
7436
|
+
|
|
7437
|
+
if not text_to_translate:
|
|
7438
|
+
# Fall back to full source text
|
|
7439
|
+
source_widget = self.table.cellWidget(current_row, 2)
|
|
7440
|
+
if not source_widget or not hasattr(source_widget, 'toPlainText'):
|
|
7441
|
+
self.log("⚠️ Could not get source text")
|
|
7442
|
+
return
|
|
7443
|
+
text_to_translate = source_widget.toPlainText().strip()
|
|
7444
|
+
|
|
7445
|
+
if not text_to_translate:
|
|
7446
|
+
self.log("⚠️ No text to translate")
|
|
7447
|
+
return
|
|
7448
|
+
|
|
7449
|
+
# Import and create the popup
|
|
7450
|
+
from modules.quicktrans import MTQuickPopup
|
|
7451
|
+
|
|
7452
|
+
# Create popup
|
|
7453
|
+
popup = MTQuickPopup(
|
|
7454
|
+
parent_app=self,
|
|
7455
|
+
source_text=text_to_translate,
|
|
7456
|
+
source_lang=getattr(self, 'source_language', 'en'),
|
|
7457
|
+
target_lang=getattr(self, 'target_language', 'nl'),
|
|
7458
|
+
parent=self
|
|
7459
|
+
)
|
|
7460
|
+
|
|
7461
|
+
# Connect signal to insert translation into target cell
|
|
7462
|
+
def insert_translation(translation: str):
|
|
7463
|
+
# Get the target widget
|
|
7464
|
+
target_widget = self.table.cellWidget(current_row, 3)
|
|
7465
|
+
if not target_widget or not hasattr(target_widget, 'toPlainText'):
|
|
7466
|
+
return
|
|
7467
|
+
|
|
7468
|
+
# Check if there was a selection in the target - replace just that
|
|
7469
|
+
focus_widget = QApplication.focusWidget()
|
|
7470
|
+
if focus_widget == target_widget and hasattr(focus_widget, 'textCursor'):
|
|
7471
|
+
cursor = focus_widget.textCursor()
|
|
7472
|
+
if cursor.hasSelection():
|
|
7473
|
+
# Replace selection only
|
|
7474
|
+
cursor.insertText(translation)
|
|
7475
|
+
self.log(f"✅ Replaced selection with MT translation")
|
|
7476
|
+
else:
|
|
7477
|
+
# No selection in target, replace entire target
|
|
7478
|
+
target_widget.setPlainText(translation)
|
|
7479
|
+
self.log(f"✅ Inserted MT translation")
|
|
7480
|
+
else:
|
|
7481
|
+
# Focus was elsewhere, replace entire target
|
|
7482
|
+
target_widget.setPlainText(translation)
|
|
7483
|
+
self.log(f"✅ Inserted MT translation")
|
|
7484
|
+
|
|
7485
|
+
# Mark segment as modified
|
|
7486
|
+
if hasattr(self, 'segments') and current_row < len(self.segments):
|
|
7487
|
+
self.segments[current_row].target = target_widget.toPlainText()
|
|
7488
|
+
self.mark_segment_modified(current_row)
|
|
7489
|
+
|
|
7490
|
+
popup.translation_selected.connect(insert_translation)
|
|
7491
|
+
|
|
7492
|
+
# Show the popup (it positions itself near cursor)
|
|
7493
|
+
popup.show()
|
|
7494
|
+
|
|
7495
|
+
except ImportError as e:
|
|
7496
|
+
self.log(f"❌ MT Quick Popup module not found: {e}")
|
|
7497
|
+
except Exception as e:
|
|
7498
|
+
self.log(f"❌ Error showing MT Quick Popup: {e}")
|
|
7499
|
+
|
|
7359
7500
|
def refresh_shortcut_enabled_states(self):
|
|
7360
7501
|
"""Refresh enabled/disabled states and key bindings of all global shortcuts from shortcut manager.
|
|
7361
7502
|
|
|
@@ -7991,7 +8132,7 @@ class SupervertalerQt(QMainWindow):
|
|
|
7991
8132
|
edit_menu.addSeparator()
|
|
7992
8133
|
|
|
7993
8134
|
# Superlookup
|
|
7994
|
-
superlookup_action = QAction("🔍 &
|
|
8135
|
+
superlookup_action = QAction("🔍 &SuperLookup...", self)
|
|
7995
8136
|
superlookup_action.setShortcut("Ctrl+Alt+L")
|
|
7996
8137
|
# Tab indices: Grid=0, Project resources=1, Tools=2, Settings=3
|
|
7997
8138
|
superlookup_action.triggered.connect(lambda: self._go_to_superlookup() if hasattr(self, 'main_tabs') else None) # Navigate to Superlookup
|
|
@@ -8098,7 +8239,7 @@ class SupervertalerQt(QMainWindow):
|
|
|
8098
8239
|
# Termview visibility toggle
|
|
8099
8240
|
self.termview_visible_action = QAction("🔍 &Termview Under Grid", self)
|
|
8100
8241
|
self.termview_visible_action.setCheckable(True)
|
|
8101
|
-
self.termview_visible_action.setChecked(
|
|
8242
|
+
self.termview_visible_action.setChecked(False) # Default: hidden (restored from settings if enabled)
|
|
8102
8243
|
self.termview_visible_action.triggered.connect(self.toggle_termview_under_grid)
|
|
8103
8244
|
self.termview_visible_action.setToolTip("Show/hide the Termview panel under the grid")
|
|
8104
8245
|
view_menu.addAction(self.termview_visible_action)
|
|
@@ -10138,7 +10279,7 @@ class SupervertalerQt(QMainWindow):
|
|
|
10138
10279
|
|
|
10139
10280
|
# Create detached window
|
|
10140
10281
|
self.lookup_detached_window = QDialog(self)
|
|
10141
|
-
self.lookup_detached_window.setWindowTitle("🔍
|
|
10282
|
+
self.lookup_detached_window.setWindowTitle("🔍 SuperLookup - Supervertaler")
|
|
10142
10283
|
self.lookup_detached_window.setMinimumSize(600, 700)
|
|
10143
10284
|
self.lookup_detached_window.resize(700, 800)
|
|
10144
10285
|
|
|
@@ -10192,7 +10333,7 @@ class SupervertalerQt(QMainWindow):
|
|
|
10192
10333
|
# Header with reattach button
|
|
10193
10334
|
header_layout = QVBoxLayout()
|
|
10194
10335
|
|
|
10195
|
-
header_title = QLabel("🔍
|
|
10336
|
+
header_title = QLabel("🔍 SuperLookup")
|
|
10196
10337
|
header_title.setStyleSheet("font-size: 16px; font-weight: bold; color: #333;")
|
|
10197
10338
|
header_layout.addWidget(header_title)
|
|
10198
10339
|
|
|
@@ -10346,7 +10487,7 @@ class SupervertalerQt(QMainWindow):
|
|
|
10346
10487
|
|
|
10347
10488
|
lookup_tab = SuperlookupTab(self, user_data_path=self.user_data_path)
|
|
10348
10489
|
self.lookup_tab = lookup_tab # Store reference for later use
|
|
10349
|
-
modules_tabs.addTab(lookup_tab, "🔍
|
|
10490
|
+
modules_tabs.addTab(lookup_tab, "🔍 SuperLookup")
|
|
10350
10491
|
|
|
10351
10492
|
# Supervoice - Voice Commands & Dictation
|
|
10352
10493
|
supervoice_tab = self._create_voice_dictation_settings_tab()
|
|
@@ -14995,7 +15136,7 @@ class SupervertalerQt(QMainWindow):
|
|
|
14995
15136
|
|
|
14996
15137
|
# Delete selected term button
|
|
14997
15138
|
delete_term_btn = QPushButton("🗑️ Delete Selected Term")
|
|
14998
|
-
delete_term_btn.setStyleSheet("background-color: #f44336; color: white; font-weight: bold;")
|
|
15139
|
+
delete_term_btn.setStyleSheet("background-color: #f44336; color: white; font-weight: bold; padding: 3px 5px;")
|
|
14999
15140
|
def delete_selected_term():
|
|
15000
15141
|
selected_row = terms_table.currentRow()
|
|
15001
15142
|
if selected_row < 0:
|
|
@@ -15669,8 +15810,13 @@ class SupervertalerQt(QMainWindow):
|
|
|
15669
15810
|
# ===== TAB 4: MT Settings =====
|
|
15670
15811
|
mt_tab = self._create_mt_settings_tab()
|
|
15671
15812
|
settings_tabs.addTab(scroll_area_wrapper(mt_tab), "🌐 MT Settings")
|
|
15672
|
-
|
|
15673
|
-
# ===== TAB 5:
|
|
15813
|
+
|
|
15814
|
+
# ===== TAB 5: MT Quick Lookup Settings =====
|
|
15815
|
+
mt_quick_tab = self._create_mt_quick_lookup_settings_tab()
|
|
15816
|
+
settings_tabs.addTab(scroll_area_wrapper(mt_quick_tab), "⚡ QuickTrans")
|
|
15817
|
+
self.mt_quick_lookup_tab_index = settings_tabs.count() - 1 # Store index for opening
|
|
15818
|
+
|
|
15819
|
+
# ===== TAB 6: View Settings =====
|
|
15674
15820
|
view_tab = self._create_view_settings_tab()
|
|
15675
15821
|
settings_tabs.addTab(scroll_area_wrapper(view_tab), "🔍 View Settings")
|
|
15676
15822
|
|
|
@@ -16478,9 +16624,199 @@ class SupervertalerQt(QMainWindow):
|
|
|
16478
16624
|
layout.addWidget(save_btn)
|
|
16479
16625
|
|
|
16480
16626
|
layout.addStretch()
|
|
16481
|
-
|
|
16627
|
+
|
|
16482
16628
|
return tab
|
|
16483
|
-
|
|
16629
|
+
|
|
16630
|
+
def _create_mt_quick_lookup_settings_tab(self):
|
|
16631
|
+
"""Create MT Quick Lookup settings tab content"""
|
|
16632
|
+
from PyQt6.QtWidgets import QCheckBox, QGroupBox, QPushButton, QComboBox
|
|
16633
|
+
|
|
16634
|
+
tab = QWidget()
|
|
16635
|
+
layout = QVBoxLayout(tab)
|
|
16636
|
+
layout.setContentsMargins(20, 20, 20, 20)
|
|
16637
|
+
layout.setSpacing(15)
|
|
16638
|
+
|
|
16639
|
+
# Load current settings
|
|
16640
|
+
general_settings = self.load_general_settings()
|
|
16641
|
+
mt_quick_settings = general_settings.get('mt_quick_lookup', {})
|
|
16642
|
+
api_keys = self.load_api_keys()
|
|
16643
|
+
enabled_providers = self.load_provider_enabled_states()
|
|
16644
|
+
|
|
16645
|
+
# Header info
|
|
16646
|
+
header_info = QLabel(
|
|
16647
|
+
"⚡ <b>QuickTrans</b> - Configure which providers appear in the QuickTrans popup (Ctrl+M / Ctrl+Alt+M).<br>"
|
|
16648
|
+
"Enable MT engines and/or LLMs to get instant translation suggestions."
|
|
16649
|
+
)
|
|
16650
|
+
header_info.setTextFormat(Qt.TextFormat.RichText)
|
|
16651
|
+
header_info.setStyleSheet("font-size: 9pt; color: #444; padding: 10px; background-color: #E3F2FD; border-radius: 4px;")
|
|
16652
|
+
header_info.setWordWrap(True)
|
|
16653
|
+
layout.addWidget(header_info)
|
|
16654
|
+
|
|
16655
|
+
# ===== MT Providers Group =====
|
|
16656
|
+
mt_group = QGroupBox("🌐 Machine Translation Providers")
|
|
16657
|
+
mt_layout = QVBoxLayout()
|
|
16658
|
+
|
|
16659
|
+
mt_info = QLabel("Select which MT engines to query. Only enabled providers with valid API keys are shown.")
|
|
16660
|
+
mt_info.setWordWrap(True)
|
|
16661
|
+
mt_info.setStyleSheet("font-size: 8pt; color: #666; padding-bottom: 8px;")
|
|
16662
|
+
mt_layout.addWidget(mt_info)
|
|
16663
|
+
|
|
16664
|
+
# MT provider checkboxes
|
|
16665
|
+
self._mtql_checkboxes = {}
|
|
16666
|
+
|
|
16667
|
+
mt_providers = [
|
|
16668
|
+
("gt", "Google Translate", "mt_google_translate", "google_translate"),
|
|
16669
|
+
("dl", "DeepL", "mt_deepl", "deepl"),
|
|
16670
|
+
("ms", "Microsoft Translator", "mt_microsoft", "microsoft_translate"),
|
|
16671
|
+
("at", "Amazon Translate", "mt_amazon", "amazon_translate"),
|
|
16672
|
+
("mmt", "ModernMT", "mt_modernmt", "modernmt"),
|
|
16673
|
+
("mm", "MyMemory (Free)", "mt_mymemory", None),
|
|
16674
|
+
]
|
|
16675
|
+
|
|
16676
|
+
for code, name, enabled_key, api_key_name in mt_providers:
|
|
16677
|
+
# Check if provider is available (has API key or doesn't need one)
|
|
16678
|
+
has_key = api_key_name is None or bool(api_keys.get(api_key_name))
|
|
16679
|
+
is_enabled_globally = enabled_providers.get(enabled_key, True)
|
|
16680
|
+
|
|
16681
|
+
checkbox = CheckmarkCheckBox(name)
|
|
16682
|
+
# Default: use global MT enabled state
|
|
16683
|
+
checkbox.setChecked(mt_quick_settings.get(f"mtql_{code}", is_enabled_globally and has_key))
|
|
16684
|
+
checkbox.setEnabled(has_key)
|
|
16685
|
+
|
|
16686
|
+
if not has_key:
|
|
16687
|
+
checkbox.setToolTip(f"API key not configured for {name}")
|
|
16688
|
+
checkbox.setStyleSheet("color: #999;")
|
|
16689
|
+
else:
|
|
16690
|
+
checkbox.setToolTip(f"Include {name} in QuickTrans results")
|
|
16691
|
+
|
|
16692
|
+
self._mtql_checkboxes[f"mtql_{code}"] = checkbox
|
|
16693
|
+
mt_layout.addWidget(checkbox)
|
|
16694
|
+
|
|
16695
|
+
mt_group.setLayout(mt_layout)
|
|
16696
|
+
layout.addWidget(mt_group)
|
|
16697
|
+
|
|
16698
|
+
# ===== LLM Providers Group =====
|
|
16699
|
+
llm_group = QGroupBox("🤖 AI/LLM Providers")
|
|
16700
|
+
llm_layout = QVBoxLayout()
|
|
16701
|
+
|
|
16702
|
+
llm_info = QLabel(
|
|
16703
|
+
"Enable AI models for translation suggestions. LLMs may provide more context-aware translations but are slower.<br>"
|
|
16704
|
+
"<b>Note:</b> LLM calls cost more than MT APIs. Use sparingly for quick lookups."
|
|
16705
|
+
)
|
|
16706
|
+
llm_info.setTextFormat(Qt.TextFormat.RichText)
|
|
16707
|
+
llm_info.setWordWrap(True)
|
|
16708
|
+
llm_info.setStyleSheet("font-size: 8pt; color: #666; padding-bottom: 8px;")
|
|
16709
|
+
llm_layout.addWidget(llm_info)
|
|
16710
|
+
|
|
16711
|
+
# LLM provider checkboxes with model selection
|
|
16712
|
+
self._mtql_llm_combos = {}
|
|
16713
|
+
|
|
16714
|
+
llm_providers = [
|
|
16715
|
+
("claude", "Claude", "claude", [
|
|
16716
|
+
("claude-sonnet-4-5-20250929", "Claude Sonnet 4.5 (Recommended)"),
|
|
16717
|
+
("claude-haiku-4-5-20251001", "Claude Haiku 4.5 (Fast)"),
|
|
16718
|
+
("claude-opus-4-1-20250924", "Claude Opus 4.1 (Premium)"),
|
|
16719
|
+
]),
|
|
16720
|
+
("openai", "OpenAI", "openai", [
|
|
16721
|
+
("gpt-4o", "GPT-4o (Recommended)"),
|
|
16722
|
+
("gpt-4o-mini", "GPT-4o Mini (Fast)"),
|
|
16723
|
+
("gpt-4-turbo", "GPT-4 Turbo"),
|
|
16724
|
+
("o1", "o1 (Reasoning)"),
|
|
16725
|
+
]),
|
|
16726
|
+
("gemini", "Gemini", "gemini", [
|
|
16727
|
+
("gemini-2.5-flash", "Gemini 2.5 Flash (Recommended)"),
|
|
16728
|
+
("gemini-2.5-pro", "Gemini 2.5 Pro"),
|
|
16729
|
+
("gemini-2.0-flash", "Gemini 2.0 Flash"),
|
|
16730
|
+
]),
|
|
16731
|
+
]
|
|
16732
|
+
|
|
16733
|
+
for code, name, api_key_name, models in llm_providers:
|
|
16734
|
+
has_key = bool(api_keys.get(api_key_name))
|
|
16735
|
+
|
|
16736
|
+
# Container for checkbox and model combo
|
|
16737
|
+
llm_row = QHBoxLayout()
|
|
16738
|
+
|
|
16739
|
+
checkbox = CheckmarkCheckBox(name)
|
|
16740
|
+
# Default: disabled (LLMs are opt-in)
|
|
16741
|
+
checkbox.setChecked(mt_quick_settings.get(f"mtql_{code}", False))
|
|
16742
|
+
checkbox.setEnabled(has_key)
|
|
16743
|
+
|
|
16744
|
+
if not has_key:
|
|
16745
|
+
checkbox.setToolTip(f"API key not configured for {name}. Add it in AI Settings.")
|
|
16746
|
+
checkbox.setStyleSheet("color: #999;")
|
|
16747
|
+
else:
|
|
16748
|
+
checkbox.setToolTip(f"Include {name} translations in QuickTrans")
|
|
16749
|
+
|
|
16750
|
+
self._mtql_checkboxes[f"mtql_{code}"] = checkbox
|
|
16751
|
+
llm_row.addWidget(checkbox)
|
|
16752
|
+
|
|
16753
|
+
# Model selection combo
|
|
16754
|
+
model_combo = QComboBox()
|
|
16755
|
+
model_combo.setMinimumWidth(200)
|
|
16756
|
+
for model_id, model_name in models:
|
|
16757
|
+
model_combo.addItem(model_name, model_id)
|
|
16758
|
+
|
|
16759
|
+
# Restore saved model selection
|
|
16760
|
+
saved_model = mt_quick_settings.get(f"mtql_{code}_model")
|
|
16761
|
+
if saved_model:
|
|
16762
|
+
idx = model_combo.findData(saved_model)
|
|
16763
|
+
if idx >= 0:
|
|
16764
|
+
model_combo.setCurrentIndex(idx)
|
|
16765
|
+
|
|
16766
|
+
model_combo.setEnabled(has_key)
|
|
16767
|
+
self._mtql_llm_combos[f"mtql_{code}_model"] = model_combo
|
|
16768
|
+
llm_row.addWidget(model_combo)
|
|
16769
|
+
|
|
16770
|
+
llm_row.addStretch()
|
|
16771
|
+
llm_layout.addLayout(llm_row)
|
|
16772
|
+
|
|
16773
|
+
llm_group.setLayout(llm_layout)
|
|
16774
|
+
layout.addWidget(llm_group)
|
|
16775
|
+
|
|
16776
|
+
# Save button
|
|
16777
|
+
save_btn = QPushButton("💾 Save QuickTrans Settings")
|
|
16778
|
+
save_btn.setStyleSheet("font-weight: bold; padding: 8px;")
|
|
16779
|
+
save_btn.clicked.connect(self._save_mt_quick_lookup_settings)
|
|
16780
|
+
layout.addWidget(save_btn)
|
|
16781
|
+
|
|
16782
|
+
layout.addStretch()
|
|
16783
|
+
|
|
16784
|
+
return tab
|
|
16785
|
+
|
|
16786
|
+
def _save_mt_quick_lookup_settings(self):
|
|
16787
|
+
"""Save MT Quick Lookup settings"""
|
|
16788
|
+
general_settings = self.load_general_settings()
|
|
16789
|
+
|
|
16790
|
+
mt_quick_settings = {}
|
|
16791
|
+
|
|
16792
|
+
# Save MT provider states
|
|
16793
|
+
for key, checkbox in self._mtql_checkboxes.items():
|
|
16794
|
+
mt_quick_settings[key] = checkbox.isChecked()
|
|
16795
|
+
|
|
16796
|
+
# Save LLM model selections
|
|
16797
|
+
for key, combo in self._mtql_llm_combos.items():
|
|
16798
|
+
mt_quick_settings[key] = combo.currentData()
|
|
16799
|
+
|
|
16800
|
+
general_settings['mt_quick_lookup'] = mt_quick_settings
|
|
16801
|
+
self.save_general_settings(general_settings)
|
|
16802
|
+
|
|
16803
|
+
self.log("✓ QuickTrans settings saved")
|
|
16804
|
+
QMessageBox.information(self, "Settings Saved", "QuickTrans settings have been saved.")
|
|
16805
|
+
|
|
16806
|
+
def open_mt_quick_lookup_settings(self):
|
|
16807
|
+
"""Open Settings and navigate to MT Quick Lookup tab"""
|
|
16808
|
+
# Switch to Settings tab
|
|
16809
|
+
if hasattr(self, 'main_tabs'):
|
|
16810
|
+
# Find Settings tab index
|
|
16811
|
+
for i in range(self.main_tabs.count()):
|
|
16812
|
+
if "Settings" in self.main_tabs.tabText(i):
|
|
16813
|
+
self.main_tabs.setCurrentIndex(i)
|
|
16814
|
+
break
|
|
16815
|
+
|
|
16816
|
+
# Navigate to MT Quick Lookup sub-tab
|
|
16817
|
+
if hasattr(self, 'settings_tabs') and hasattr(self, 'mt_quick_lookup_tab_index'):
|
|
16818
|
+
self.settings_tabs.setCurrentIndex(self.mt_quick_lookup_tab_index)
|
|
16819
|
+
|
|
16484
16820
|
def _find_autohotkey_for_settings(self):
|
|
16485
16821
|
"""Find AutoHotkey executable for settings display (doesn't modify state)"""
|
|
16486
16822
|
# Standard installation paths
|
|
@@ -17971,14 +18307,15 @@ class SupervertalerQt(QMainWindow):
|
|
|
17971
18307
|
|
|
17972
18308
|
def _create_voice_dictation_settings_tab(self):
|
|
17973
18309
|
"""Create Supervoice Settings tab content with Voice Commands"""
|
|
17974
|
-
from PyQt6.QtWidgets import (QGroupBox, QPushButton, QComboBox, QSpinBox,
|
|
18310
|
+
from PyQt6.QtWidgets import (QGroupBox, QPushButton, QComboBox, QSpinBox,
|
|
17975
18311
|
QTableWidget, QTableWidgetItem, QHeaderView,
|
|
17976
|
-
QAbstractItemView, QCheckBox)
|
|
18312
|
+
QAbstractItemView, QCheckBox, QSplitter)
|
|
18313
|
+
from modules.keyboard_shortcuts_widget import CheckmarkCheckBox
|
|
17977
18314
|
|
|
17978
18315
|
tab = QWidget()
|
|
17979
|
-
|
|
17980
|
-
|
|
17981
|
-
|
|
18316
|
+
main_layout = QVBoxLayout(tab)
|
|
18317
|
+
main_layout.setContentsMargins(20, 20, 20, 20)
|
|
18318
|
+
main_layout.setSpacing(15)
|
|
17982
18319
|
|
|
17983
18320
|
# Load current dictation settings
|
|
17984
18321
|
dictation_settings = self.load_dictation_settings()
|
|
@@ -17991,19 +18328,33 @@ class SupervertalerQt(QMainWindow):
|
|
|
17991
18328
|
header_info.setTextFormat(Qt.TextFormat.RichText)
|
|
17992
18329
|
header_info.setStyleSheet("font-size: 9pt; color: #444; padding: 10px; background-color: #E3F2FD; border-radius: 4px;")
|
|
17993
18330
|
header_info.setWordWrap(True)
|
|
17994
|
-
|
|
18331
|
+
main_layout.addWidget(header_info)
|
|
17995
18332
|
|
|
17996
|
-
# ===== Voice Commands
|
|
17997
|
-
|
|
17998
|
-
|
|
18333
|
+
# ===== Two-column layout: Left = Settings, Right = Voice Commands Table =====
|
|
18334
|
+
columns_layout = QHBoxLayout()
|
|
18335
|
+
columns_layout.setSpacing(15)
|
|
17999
18336
|
|
|
18000
|
-
#
|
|
18001
|
-
|
|
18337
|
+
# --- LEFT COLUMN: Settings ---
|
|
18338
|
+
left_column = QVBoxLayout()
|
|
18339
|
+
left_column.setSpacing(15)
|
|
18340
|
+
|
|
18341
|
+
# Enable voice commands checkbox (green checkmark style)
|
|
18342
|
+
voice_cmd_enabled = CheckmarkCheckBox("Enable voice commands (spoken phrases trigger actions)")
|
|
18002
18343
|
voice_cmd_enabled.setChecked(dictation_settings.get('voice_commands_enabled', True))
|
|
18003
18344
|
voice_cmd_enabled.setToolTip("When enabled, spoken phrases like 'confirm' or 'next segment' will execute commands instead of being inserted as text")
|
|
18004
|
-
|
|
18345
|
+
left_column.addWidget(voice_cmd_enabled)
|
|
18005
18346
|
self.voice_commands_enabled_checkbox = voice_cmd_enabled
|
|
18006
18347
|
|
|
18348
|
+
# Create a layout variable for settings sections to be added below
|
|
18349
|
+
layout = left_column
|
|
18350
|
+
|
|
18351
|
+
# --- RIGHT COLUMN: Voice Commands Table ---
|
|
18352
|
+
right_column = QVBoxLayout()
|
|
18353
|
+
right_column.setSpacing(10)
|
|
18354
|
+
|
|
18355
|
+
commands_group = QGroupBox("🗣️ Voice Commands (Talon-style)")
|
|
18356
|
+
commands_layout = QVBoxLayout()
|
|
18357
|
+
|
|
18007
18358
|
commands_info = QLabel(
|
|
18008
18359
|
"Voice commands let you control Supervertaler by voice. Say a phrase to execute an action.\n"
|
|
18009
18360
|
"If no command matches, the spoken text is inserted as dictation."
|
|
@@ -18022,38 +18373,47 @@ class SupervertalerQt(QMainWindow):
|
|
|
18022
18373
|
self.voice_commands_table.horizontalHeader().setSectionResizeMode(3, QHeaderView.ResizeMode.ResizeToContents)
|
|
18023
18374
|
self.voice_commands_table.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
|
|
18024
18375
|
self.voice_commands_table.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers)
|
|
18025
|
-
self.voice_commands_table.setMinimumHeight(
|
|
18026
|
-
|
|
18027
|
-
|
|
18376
|
+
self.voice_commands_table.setMinimumHeight(350)
|
|
18377
|
+
|
|
18028
18378
|
# Populate table with current commands
|
|
18029
18379
|
self._populate_voice_commands_table()
|
|
18030
18380
|
commands_layout.addWidget(self.voice_commands_table)
|
|
18031
18381
|
|
|
18032
18382
|
# Command buttons
|
|
18033
18383
|
cmd_btn_layout = QHBoxLayout()
|
|
18034
|
-
|
|
18035
|
-
add_cmd_btn = QPushButton("➕ Add
|
|
18384
|
+
|
|
18385
|
+
add_cmd_btn = QPushButton("➕ Add")
|
|
18036
18386
|
add_cmd_btn.clicked.connect(self._add_voice_command)
|
|
18037
18387
|
cmd_btn_layout.addWidget(add_cmd_btn)
|
|
18038
|
-
|
|
18039
|
-
edit_cmd_btn = QPushButton("✏️ Edit
|
|
18388
|
+
|
|
18389
|
+
edit_cmd_btn = QPushButton("✏️ Edit")
|
|
18040
18390
|
edit_cmd_btn.clicked.connect(self._edit_voice_command)
|
|
18041
18391
|
cmd_btn_layout.addWidget(edit_cmd_btn)
|
|
18042
|
-
|
|
18043
|
-
remove_cmd_btn = QPushButton("🗑️ Remove
|
|
18392
|
+
|
|
18393
|
+
remove_cmd_btn = QPushButton("🗑️ Remove")
|
|
18044
18394
|
remove_cmd_btn.clicked.connect(self._remove_voice_command)
|
|
18045
18395
|
cmd_btn_layout.addWidget(remove_cmd_btn)
|
|
18046
|
-
|
|
18396
|
+
|
|
18047
18397
|
cmd_btn_layout.addStretch()
|
|
18048
|
-
|
|
18049
|
-
reset_cmd_btn = QPushButton("🔄 Reset
|
|
18398
|
+
|
|
18399
|
+
reset_cmd_btn = QPushButton("🔄 Reset")
|
|
18050
18400
|
reset_cmd_btn.clicked.connect(self._reset_voice_commands)
|
|
18051
18401
|
cmd_btn_layout.addWidget(reset_cmd_btn)
|
|
18052
|
-
|
|
18402
|
+
|
|
18053
18403
|
commands_layout.addLayout(cmd_btn_layout)
|
|
18054
18404
|
|
|
18055
18405
|
commands_group.setLayout(commands_layout)
|
|
18056
|
-
|
|
18406
|
+
right_column.addWidget(commands_group)
|
|
18407
|
+
right_column.addStretch()
|
|
18408
|
+
|
|
18409
|
+
# Add columns to the two-column layout
|
|
18410
|
+
left_widget = QWidget()
|
|
18411
|
+
left_widget.setLayout(left_column)
|
|
18412
|
+
right_widget = QWidget()
|
|
18413
|
+
right_widget.setLayout(right_column)
|
|
18414
|
+
|
|
18415
|
+
columns_layout.addWidget(left_widget, stretch=1)
|
|
18416
|
+
columns_layout.addWidget(right_widget, stretch=1)
|
|
18057
18417
|
|
|
18058
18418
|
# ===== Always-On Mode Section =====
|
|
18059
18419
|
alwayson_group = QGroupBox("🎧 Always-On Listening Mode")
|
|
@@ -18216,7 +18576,13 @@ class SupervertalerQt(QMainWindow):
|
|
|
18216
18576
|
ahk_group.setLayout(ahk_layout)
|
|
18217
18577
|
layout.addWidget(ahk_group)
|
|
18218
18578
|
|
|
18219
|
-
#
|
|
18579
|
+
# Add stretch to left column to push content up
|
|
18580
|
+
layout.addStretch()
|
|
18581
|
+
|
|
18582
|
+
# Add two-column layout to main layout
|
|
18583
|
+
main_layout.addLayout(columns_layout, stretch=1)
|
|
18584
|
+
|
|
18585
|
+
# Save button (full width, below the two columns)
|
|
18220
18586
|
save_btn = QPushButton("💾 Save Supervoice Settings")
|
|
18221
18587
|
save_btn.setStyleSheet("background-color: #4CAF50; color: white; font-weight: bold; padding: 10px; border: none; outline: none;")
|
|
18222
18588
|
save_btn.clicked.connect(lambda: self._save_voice_settings(
|
|
@@ -18225,15 +18591,13 @@ class SupervertalerQt(QMainWindow):
|
|
|
18225
18591
|
lang_combo.currentText(),
|
|
18226
18592
|
voice_cmd_enabled.isChecked()
|
|
18227
18593
|
))
|
|
18228
|
-
|
|
18594
|
+
main_layout.addWidget(save_btn)
|
|
18229
18595
|
|
|
18230
18596
|
# Store references
|
|
18231
18597
|
self.dictation_model_combo = model_combo
|
|
18232
18598
|
self.dictation_duration_spin = duration_spin
|
|
18233
18599
|
self.dictation_lang_combo = lang_combo
|
|
18234
18600
|
|
|
18235
|
-
layout.addStretch()
|
|
18236
|
-
|
|
18237
18601
|
return tab
|
|
18238
18602
|
|
|
18239
18603
|
def _populate_voice_commands_table(self):
|
|
@@ -18487,7 +18851,7 @@ class SupervertalerQt(QMainWindow):
|
|
|
18487
18851
|
background-color: #2E7D32;
|
|
18488
18852
|
color: white;
|
|
18489
18853
|
font-weight: bold;
|
|
18490
|
-
padding:
|
|
18854
|
+
padding: 3px 5px;
|
|
18491
18855
|
border-radius: 3px;
|
|
18492
18856
|
}
|
|
18493
18857
|
QPushButton:checked {
|
|
@@ -18501,7 +18865,7 @@ class SupervertalerQt(QMainWindow):
|
|
|
18501
18865
|
background-color: #C62828;
|
|
18502
18866
|
color: white;
|
|
18503
18867
|
font-weight: bold;
|
|
18504
|
-
padding:
|
|
18868
|
+
padding: 3px 5px;
|
|
18505
18869
|
border-radius: 3px;
|
|
18506
18870
|
}
|
|
18507
18871
|
QPushButton:checked {
|
|
@@ -18515,7 +18879,7 @@ class SupervertalerQt(QMainWindow):
|
|
|
18515
18879
|
background-color: #F57C00;
|
|
18516
18880
|
color: white;
|
|
18517
18881
|
font-weight: bold;
|
|
18518
|
-
padding:
|
|
18882
|
+
padding: 3px 5px;
|
|
18519
18883
|
border-radius: 3px;
|
|
18520
18884
|
}
|
|
18521
18885
|
QPushButton:checked {
|
|
@@ -18530,7 +18894,7 @@ class SupervertalerQt(QMainWindow):
|
|
|
18530
18894
|
background-color: #757575;
|
|
18531
18895
|
color: white;
|
|
18532
18896
|
font-weight: bold;
|
|
18533
|
-
padding:
|
|
18897
|
+
padding: 3px 5px;
|
|
18534
18898
|
border-radius: 3px;
|
|
18535
18899
|
}
|
|
18536
18900
|
QPushButton:checked {
|
|
@@ -19864,6 +20228,27 @@ class SupervertalerQt(QMainWindow):
|
|
|
19864
20228
|
grid_font_family_combo=None, termview_font_family_combo=None, termview_font_spin=None, termview_bold_check=None,
|
|
19865
20229
|
border_color_btn=None, border_thickness_spin=None, badge_text_color_btn=None, tabs_above_check=None):
|
|
19866
20230
|
"""Save view settings from UI"""
|
|
20231
|
+
# CRITICAL: Suppress TM saves during view settings update
|
|
20232
|
+
# Grid operations (setStyleSheet, rehighlight, etc.) can trigger textChanged events
|
|
20233
|
+
# which would cause mass TM saves for all confirmed segments
|
|
20234
|
+
previous_suppression = getattr(self, '_suppress_target_change_handlers', False)
|
|
20235
|
+
self._suppress_target_change_handlers = True
|
|
20236
|
+
|
|
20237
|
+
try:
|
|
20238
|
+
self._save_view_settings_from_ui_impl(
|
|
20239
|
+
grid_spin, match_spin, compare_spin, show_tags_check, tag_color_btn,
|
|
20240
|
+
alt_colors_check, even_color_btn, odd_color_btn, invisible_char_color_btn,
|
|
20241
|
+
grid_font_family_combo, termview_font_family_combo, termview_font_spin, termview_bold_check,
|
|
20242
|
+
border_color_btn, border_thickness_spin, badge_text_color_btn, tabs_above_check
|
|
20243
|
+
)
|
|
20244
|
+
finally:
|
|
20245
|
+
self._suppress_target_change_handlers = previous_suppression
|
|
20246
|
+
|
|
20247
|
+
def _save_view_settings_from_ui_impl(self, grid_spin, match_spin, compare_spin, show_tags_check=None, tag_color_btn=None,
|
|
20248
|
+
alt_colors_check=None, even_color_btn=None, odd_color_btn=None, invisible_char_color_btn=None,
|
|
20249
|
+
grid_font_family_combo=None, termview_font_family_combo=None, termview_font_spin=None, termview_bold_check=None,
|
|
20250
|
+
border_color_btn=None, border_thickness_spin=None, badge_text_color_btn=None, tabs_above_check=None):
|
|
20251
|
+
"""Implementation of save view settings (called with TM saves suppressed)"""
|
|
19867
20252
|
general_settings = {
|
|
19868
20253
|
'restore_last_project': self.load_general_settings().get('restore_last_project', False),
|
|
19869
20254
|
'auto_propagate_exact_matches': self.auto_propagate_exact_matches, # Keep existing value
|
|
@@ -20034,8 +20419,10 @@ class SupervertalerQt(QMainWindow):
|
|
|
20034
20419
|
if invisible_char_color_btn and hasattr(self, 'table') and self.table is not None:
|
|
20035
20420
|
invisible_char_color = invisible_char_color_btn.property('selected_color')
|
|
20036
20421
|
if invisible_char_color:
|
|
20037
|
-
# Update all cell highlighters
|
|
20422
|
+
# Update all cell highlighters (with processEvents to keep UI responsive)
|
|
20038
20423
|
for row in range(self.table.rowCount()):
|
|
20424
|
+
if row % 50 == 0:
|
|
20425
|
+
QApplication.processEvents()
|
|
20039
20426
|
for col in [2, 3]: # Source and target columns
|
|
20040
20427
|
widget = self.table.cellWidget(row, col)
|
|
20041
20428
|
if widget and hasattr(widget, 'highlighter'):
|
|
@@ -20047,8 +20434,10 @@ class SupervertalerQt(QMainWindow):
|
|
|
20047
20434
|
border_color = EditableGridTextEditor.focus_border_color
|
|
20048
20435
|
border_thickness = EditableGridTextEditor.focus_border_thickness
|
|
20049
20436
|
self.log(f"Applying focus border: color={border_color}, thickness={border_thickness}px")
|
|
20050
|
-
|
|
20437
|
+
|
|
20051
20438
|
for row in range(self.table.rowCount()):
|
|
20439
|
+
if row % 50 == 0:
|
|
20440
|
+
QApplication.processEvents()
|
|
20052
20441
|
widget = self.table.cellWidget(row, 3) # Target column
|
|
20053
20442
|
if widget and isinstance(widget, EditableGridTextEditor):
|
|
20054
20443
|
# Update the stylesheet with new border settings
|
|
@@ -20154,7 +20543,7 @@ class SupervertalerQt(QMainWindow):
|
|
|
20154
20543
|
filter_layout.setSpacing(10)
|
|
20155
20544
|
|
|
20156
20545
|
# Source filter
|
|
20157
|
-
source_filter_label = QLabel("
|
|
20546
|
+
source_filter_label = QLabel("Source:")
|
|
20158
20547
|
self.source_filter = self._ensure_shared_filter(
|
|
20159
20548
|
'source_filter',
|
|
20160
20549
|
"Type to filter source segments...",
|
|
@@ -20163,7 +20552,7 @@ class SupervertalerQt(QMainWindow):
|
|
|
20163
20552
|
)
|
|
20164
20553
|
|
|
20165
20554
|
# Target filter
|
|
20166
|
-
target_filter_label = QLabel("
|
|
20555
|
+
target_filter_label = QLabel("Target:")
|
|
20167
20556
|
self.target_filter = self._ensure_shared_filter(
|
|
20168
20557
|
'target_filter',
|
|
20169
20558
|
"Type to filter target segments...",
|
|
@@ -20175,11 +20564,12 @@ class SupervertalerQt(QMainWindow):
|
|
|
20175
20564
|
clear_filters_btn = QPushButton("Clear Filters")
|
|
20176
20565
|
clear_filters_btn.clicked.connect(self.clear_filters)
|
|
20177
20566
|
clear_filters_btn.setMaximumWidth(100)
|
|
20567
|
+
clear_filters_btn.setStyleSheet("padding: 3px 5px;")
|
|
20178
20568
|
|
|
20179
20569
|
# Show Invisibles button with dropdown menu
|
|
20180
20570
|
show_invisibles_btn = QPushButton("¶ Show Invisibles")
|
|
20181
20571
|
show_invisibles_btn.setMaximumWidth(140)
|
|
20182
|
-
show_invisibles_btn.setStyleSheet("background-color: #607D8B; color: white; font-weight: bold;")
|
|
20572
|
+
show_invisibles_btn.setStyleSheet("background-color: #607D8B; color: white; font-weight: bold; padding: 3px 5px;")
|
|
20183
20573
|
show_invisibles_menu = QMenu(show_invisibles_btn)
|
|
20184
20574
|
|
|
20185
20575
|
# Create checkable actions for each invisible character type
|
|
@@ -20304,7 +20694,7 @@ class SupervertalerQt(QMainWindow):
|
|
|
20304
20694
|
filter_layout.setSpacing(10)
|
|
20305
20695
|
|
|
20306
20696
|
# Source filter
|
|
20307
|
-
source_filter_label = QLabel("
|
|
20697
|
+
source_filter_label = QLabel("Source:")
|
|
20308
20698
|
self.source_filter = self._ensure_shared_filter(
|
|
20309
20699
|
'source_filter',
|
|
20310
20700
|
"Type to filter source segments... (Press Enter or click Filter)",
|
|
@@ -20312,7 +20702,7 @@ class SupervertalerQt(QMainWindow):
|
|
|
20312
20702
|
)
|
|
20313
20703
|
|
|
20314
20704
|
# Target filter
|
|
20315
|
-
target_filter_label = QLabel("
|
|
20705
|
+
target_filter_label = QLabel("Target:")
|
|
20316
20706
|
self.target_filter = self._ensure_shared_filter(
|
|
20317
20707
|
'target_filter',
|
|
20318
20708
|
"Type to filter target segments... (Press Enter or click Filter)",
|
|
@@ -20323,17 +20713,18 @@ class SupervertalerQt(QMainWindow):
|
|
|
20323
20713
|
apply_filter_btn = QPushButton("Filter")
|
|
20324
20714
|
apply_filter_btn.clicked.connect(self.apply_filters)
|
|
20325
20715
|
apply_filter_btn.setMaximumWidth(80)
|
|
20326
|
-
apply_filter_btn.setStyleSheet("background-color: #4CAF50; color: white; font-weight: bold;")
|
|
20716
|
+
apply_filter_btn.setStyleSheet("background-color: #4CAF50; color: white; font-weight: bold; padding: 3px 5px;")
|
|
20327
20717
|
|
|
20328
20718
|
# Clear filters button
|
|
20329
20719
|
clear_filters_btn = QPushButton("Clear Filters")
|
|
20330
20720
|
clear_filters_btn.clicked.connect(self.clear_filters)
|
|
20331
20721
|
clear_filters_btn.setMaximumWidth(100)
|
|
20722
|
+
clear_filters_btn.setStyleSheet("padding: 3px 5px;")
|
|
20332
20723
|
|
|
20333
20724
|
# Quick Filters dropdown menu
|
|
20334
20725
|
quick_filter_btn = QPushButton("⚡ Quick Filters")
|
|
20335
20726
|
quick_filter_btn.setMaximumWidth(130)
|
|
20336
|
-
quick_filter_btn.setStyleSheet("background-color: #D84315; color: white; font-weight: bold;")
|
|
20727
|
+
quick_filter_btn.setStyleSheet("background-color: #D84315; color: white; font-weight: bold; padding: 3px 5px;")
|
|
20337
20728
|
quick_filter_menu = QMenu(self)
|
|
20338
20729
|
quick_filter_menu.addAction("🔍 Empty segments", lambda: self.apply_quick_filter("empty"))
|
|
20339
20730
|
quick_filter_menu.addAction("❌ Not translated", lambda: self.apply_quick_filter("not_translated"))
|
|
@@ -20347,8 +20738,69 @@ class SupervertalerQt(QMainWindow):
|
|
|
20347
20738
|
advanced_filter_btn = QPushButton("⚙️ Advanced Filters")
|
|
20348
20739
|
advanced_filter_btn.clicked.connect(self.show_advanced_filters_dialog)
|
|
20349
20740
|
advanced_filter_btn.setMaximumWidth(160)
|
|
20350
|
-
advanced_filter_btn.setStyleSheet("background-color: #2196F3; color: white; font-weight: bold;")
|
|
20351
|
-
|
|
20741
|
+
advanced_filter_btn.setStyleSheet("background-color: #2196F3; color: white; font-weight: bold; padding: 3px 5px;")
|
|
20742
|
+
|
|
20743
|
+
# Sort dropdown button (similar to memoQ)
|
|
20744
|
+
sort_btn = QPushButton("⇅ Sort")
|
|
20745
|
+
sort_btn.setMaximumWidth(100)
|
|
20746
|
+
sort_btn.setStyleSheet("background-color: #FF9800; color: white; font-weight: bold; padding: 3px 5px;")
|
|
20747
|
+
sort_menu = QMenu(self)
|
|
20748
|
+
|
|
20749
|
+
# Initialize sort state if not exists
|
|
20750
|
+
if not hasattr(self, 'current_sort'):
|
|
20751
|
+
self.current_sort = None # None = document order
|
|
20752
|
+
|
|
20753
|
+
# Sort by source text
|
|
20754
|
+
sort_menu.addAction("📝 Source A → Z", lambda: self.apply_sort('source_asc'))
|
|
20755
|
+
sort_menu.addAction("📝 Source Z → A", lambda: self.apply_sort('source_desc'))
|
|
20756
|
+
|
|
20757
|
+
sort_menu.addSeparator()
|
|
20758
|
+
|
|
20759
|
+
# Sort by target text
|
|
20760
|
+
sort_menu.addAction("📄 Target A → Z", lambda: self.apply_sort('target_asc'))
|
|
20761
|
+
sort_menu.addAction("📄 Target Z → A", lambda: self.apply_sort('target_desc'))
|
|
20762
|
+
|
|
20763
|
+
sort_menu.addSeparator()
|
|
20764
|
+
|
|
20765
|
+
# Sort by text length
|
|
20766
|
+
sort_menu.addAction("📏 Source (longer first)", lambda: self.apply_sort('source_length_desc'))
|
|
20767
|
+
sort_menu.addAction("📏 Source (shorter first)", lambda: self.apply_sort('source_length_asc'))
|
|
20768
|
+
sort_menu.addAction("📏 Target (longer first)", lambda: self.apply_sort('target_length_desc'))
|
|
20769
|
+
sort_menu.addAction("📏 Target (shorter first)", lambda: self.apply_sort('target_length_asc'))
|
|
20770
|
+
|
|
20771
|
+
sort_menu.addSeparator()
|
|
20772
|
+
|
|
20773
|
+
# Sort by match rate
|
|
20774
|
+
sort_menu.addAction("🎯 Match Rate (higher first)", lambda: self.apply_sort('match_desc'))
|
|
20775
|
+
sort_menu.addAction("🎯 Match Rate (lower first)", lambda: self.apply_sort('match_asc'))
|
|
20776
|
+
|
|
20777
|
+
sort_menu.addSeparator()
|
|
20778
|
+
|
|
20779
|
+
# Sort by frequency
|
|
20780
|
+
sort_menu.addAction("📊 Source Frequency (higher first)", lambda: self.apply_sort('source_freq_desc'))
|
|
20781
|
+
sort_menu.addAction("📊 Source Frequency (lower first)", lambda: self.apply_sort('source_freq_asc'))
|
|
20782
|
+
sort_menu.addAction("📊 Target Frequency (higher first)", lambda: self.apply_sort('target_freq_desc'))
|
|
20783
|
+
sort_menu.addAction("📊 Target Frequency (lower first)", lambda: self.apply_sort('target_freq_asc'))
|
|
20784
|
+
|
|
20785
|
+
sort_menu.addSeparator()
|
|
20786
|
+
|
|
20787
|
+
# Sort by last changed
|
|
20788
|
+
sort_menu.addAction("🕒 Last Changed (newest first)", lambda: self.apply_sort('modified_desc'))
|
|
20789
|
+
sort_menu.addAction("🕒 Last Changed (oldest first)", lambda: self.apply_sort('modified_asc'))
|
|
20790
|
+
|
|
20791
|
+
sort_menu.addSeparator()
|
|
20792
|
+
|
|
20793
|
+
# Sort by row status
|
|
20794
|
+
sort_menu.addAction("🚦 Row Status", lambda: self.apply_sort('status'))
|
|
20795
|
+
|
|
20796
|
+
sort_menu.addSeparator()
|
|
20797
|
+
|
|
20798
|
+
# Reset to document order
|
|
20799
|
+
sort_menu.addAction("↩️ Document Order (default)", lambda: self.apply_sort(None))
|
|
20800
|
+
|
|
20801
|
+
sort_btn.setMenu(sort_menu)
|
|
20802
|
+
sort_btn.setToolTip("Sort segments by various criteria")
|
|
20803
|
+
|
|
20352
20804
|
# File filter dropdown (for multi-file projects)
|
|
20353
20805
|
self.file_filter_combo = QComboBox()
|
|
20354
20806
|
self.file_filter_combo.setMinimumWidth(150)
|
|
@@ -20361,7 +20813,7 @@ class SupervertalerQt(QMainWindow):
|
|
|
20361
20813
|
# Show Invisibles button with dropdown menu
|
|
20362
20814
|
show_invisibles_btn_home = QPushButton("¶ Show Invisibles")
|
|
20363
20815
|
show_invisibles_btn_home.setMaximumWidth(140)
|
|
20364
|
-
show_invisibles_btn_home.setStyleSheet("background-color: #607D8B; color: white; font-weight: bold;")
|
|
20816
|
+
show_invisibles_btn_home.setStyleSheet("background-color: #607D8B; color: white; font-weight: bold; padding: 3px 5px;")
|
|
20365
20817
|
show_invisibles_menu_home = QMenu(show_invisibles_btn_home)
|
|
20366
20818
|
|
|
20367
20819
|
# Use the same actions (they're stored as instance variables)
|
|
@@ -20442,6 +20894,7 @@ class SupervertalerQt(QMainWindow):
|
|
|
20442
20894
|
filter_layout.addWidget(clear_filters_btn)
|
|
20443
20895
|
filter_layout.addWidget(quick_filter_btn)
|
|
20444
20896
|
filter_layout.addWidget(advanced_filter_btn)
|
|
20897
|
+
filter_layout.addWidget(sort_btn) # Sort dropdown
|
|
20445
20898
|
filter_layout.addWidget(self.file_filter_combo) # File filter for multi-file projects
|
|
20446
20899
|
filter_layout.addWidget(show_invisibles_btn_home)
|
|
20447
20900
|
filter_layout.addWidget(self.spellcheck_btn)
|
|
@@ -20565,30 +21018,80 @@ class SupervertalerQt(QMainWindow):
|
|
|
20565
21018
|
tab_seg_info.setStyleSheet("font-weight: bold;")
|
|
20566
21019
|
toolbar_layout.addWidget(tab_seg_info)
|
|
20567
21020
|
|
|
20568
|
-
#
|
|
20569
|
-
|
|
20570
|
-
|
|
20571
|
-
|
|
20572
|
-
|
|
21021
|
+
# View mode segmented control (WYSIWYG / Tags)
|
|
21022
|
+
from PyQt6.QtWidgets import QButtonGroup
|
|
21023
|
+
|
|
21024
|
+
view_mode_container = QWidget()
|
|
21025
|
+
view_mode_layout = QHBoxLayout(view_mode_container)
|
|
21026
|
+
view_mode_layout.setContentsMargins(0, 0, 0, 0)
|
|
21027
|
+
view_mode_layout.setSpacing(0)
|
|
21028
|
+
|
|
21029
|
+
# Create button group to ensure only one is checked
|
|
21030
|
+
view_mode_group = QButtonGroup(self)
|
|
21031
|
+
view_mode_group.setExclusive(True)
|
|
21032
|
+
|
|
21033
|
+
# WYSIWYG button (left)
|
|
21034
|
+
wysiwyg_btn = QPushButton("WYSIWYG")
|
|
21035
|
+
wysiwyg_btn.setCheckable(True)
|
|
21036
|
+
wysiwyg_btn.setChecked(False)
|
|
21037
|
+
wysiwyg_btn.setToolTip("WYSIWYG View (Ctrl+Alt+T)\nShows formatted text without raw tags")
|
|
21038
|
+
wysiwyg_btn.setStyleSheet("""
|
|
20573
21039
|
QPushButton {
|
|
20574
21040
|
background-color: #757575;
|
|
20575
21041
|
color: white;
|
|
20576
21042
|
font-weight: bold;
|
|
20577
|
-
padding: 4px
|
|
20578
|
-
border
|
|
21043
|
+
padding: 4px 12px;
|
|
21044
|
+
border: none;
|
|
21045
|
+
border-top-left-radius: 3px;
|
|
21046
|
+
border-bottom-left-radius: 3px;
|
|
21047
|
+
}
|
|
21048
|
+
QPushButton:checked {
|
|
21049
|
+
background-color: #9C27B0;
|
|
21050
|
+
}
|
|
21051
|
+
QPushButton:hover:!checked {
|
|
21052
|
+
background-color: #858585;
|
|
21053
|
+
}
|
|
21054
|
+
""")
|
|
21055
|
+
wysiwyg_btn.clicked.connect(lambda: self.toggle_tag_view(False, None))
|
|
21056
|
+
view_mode_group.addButton(wysiwyg_btn, 0)
|
|
21057
|
+
view_mode_layout.addWidget(wysiwyg_btn)
|
|
21058
|
+
|
|
21059
|
+
# Tags button (right)
|
|
21060
|
+
tags_btn = QPushButton("Tags")
|
|
21061
|
+
tags_btn.setCheckable(True)
|
|
21062
|
+
tags_btn.setChecked(True) # Default: Tags mode
|
|
21063
|
+
tags_btn.setToolTip("Tag View (Ctrl+Alt+T)\nShows raw tags like <b>bold</b>")
|
|
21064
|
+
tags_btn.setStyleSheet("""
|
|
21065
|
+
QPushButton {
|
|
21066
|
+
background-color: #757575;
|
|
21067
|
+
color: white;
|
|
21068
|
+
font-weight: bold;
|
|
21069
|
+
padding: 4px 12px;
|
|
21070
|
+
border: none;
|
|
21071
|
+
border-top-right-radius: 3px;
|
|
21072
|
+
border-bottom-right-radius: 3px;
|
|
20579
21073
|
}
|
|
20580
21074
|
QPushButton:checked {
|
|
20581
21075
|
background-color: #9C27B0;
|
|
20582
21076
|
}
|
|
21077
|
+
QPushButton:hover:!checked {
|
|
21078
|
+
background-color: #858585;
|
|
21079
|
+
}
|
|
20583
21080
|
""")
|
|
20584
|
-
|
|
20585
|
-
|
|
20586
|
-
|
|
20587
|
-
|
|
21081
|
+
tags_btn.clicked.connect(lambda: self.toggle_tag_view(True, None))
|
|
21082
|
+
view_mode_group.addButton(tags_btn, 1)
|
|
21083
|
+
view_mode_layout.addWidget(tags_btn)
|
|
21084
|
+
|
|
21085
|
+
toolbar_layout.addWidget(view_mode_container)
|
|
21086
|
+
|
|
21087
|
+
# Store references for keyboard shortcut and programmatic access
|
|
21088
|
+
self.wysiwyg_btn = wysiwyg_btn
|
|
21089
|
+
self.tags_btn = tags_btn
|
|
21090
|
+
self.view_mode_group = view_mode_group
|
|
20588
21091
|
|
|
20589
21092
|
# Initialize tag view state
|
|
20590
21093
|
if not hasattr(self, 'show_tags'):
|
|
20591
|
-
self.show_tags =
|
|
21094
|
+
self.show_tags = True # Default: show tags
|
|
20592
21095
|
|
|
20593
21096
|
# Status selector
|
|
20594
21097
|
from modules.statuses import get_status, STATUSES
|
|
@@ -20606,12 +21109,12 @@ class SupervertalerQt(QMainWindow):
|
|
|
20606
21109
|
|
|
20607
21110
|
preview_prompt_btn = QPushButton("🧪 Preview Prompts")
|
|
20608
21111
|
preview_prompt_btn.setToolTip("Preview the complete assembled prompt\n(System Prompt + Custom Prompts + current segment)")
|
|
20609
|
-
preview_prompt_btn.setStyleSheet("background-color: #9C27B0; color: white; font-weight: bold; padding:
|
|
21112
|
+
preview_prompt_btn.setStyleSheet("background-color: #9C27B0; color: white; font-weight: bold; padding: 3px 5px; border: none; outline: none;")
|
|
20610
21113
|
preview_prompt_btn.clicked.connect(self._preview_combined_prompt_from_grid)
|
|
20611
21114
|
toolbar_layout.addWidget(preview_prompt_btn)
|
|
20612
21115
|
|
|
20613
21116
|
dictate_btn = QPushButton("🎤 Dictation")
|
|
20614
|
-
dictate_btn.setStyleSheet("background-color: #4CAF50; color: white; font-weight: bold; padding:
|
|
21117
|
+
dictate_btn.setStyleSheet("background-color: #4CAF50; color: white; font-weight: bold; padding: 3px 5px; border: none; outline: none;")
|
|
20615
21118
|
dictate_btn.clicked.connect(self.start_voice_dictation)
|
|
20616
21119
|
dictate_btn.setToolTip("Start/stop voice dictation (F9)")
|
|
20617
21120
|
toolbar_layout.addWidget(dictate_btn)
|
|
@@ -20625,7 +21128,7 @@ class SupervertalerQt(QMainWindow):
|
|
|
20625
21128
|
background-color: #757575;
|
|
20626
21129
|
color: white;
|
|
20627
21130
|
font-weight: bold;
|
|
20628
|
-
padding:
|
|
21131
|
+
padding: 3px 5px;
|
|
20629
21132
|
border-radius: 3px;
|
|
20630
21133
|
}
|
|
20631
21134
|
QPushButton:checked {
|
|
@@ -20640,7 +21143,7 @@ class SupervertalerQt(QMainWindow):
|
|
|
20640
21143
|
toolbar_layout.addStretch()
|
|
20641
21144
|
|
|
20642
21145
|
save_next_btn = QPushButton("✓ Confirm && Next")
|
|
20643
|
-
save_next_btn.setStyleSheet("background-color: #2196F3; color: white; font-weight: bold; padding:
|
|
21146
|
+
save_next_btn.setStyleSheet("background-color: #2196F3; color: white; font-weight: bold; padding: 3px 5px; border: none; outline: none;")
|
|
20644
21147
|
save_next_btn.clicked.connect(self.confirm_selected_or_next)
|
|
20645
21148
|
save_next_btn.setToolTip("Confirm current segment and go to next unconfirmed (Ctrl+Enter)")
|
|
20646
21149
|
toolbar_layout.addWidget(save_next_btn)
|
|
@@ -21249,9 +21752,21 @@ class SupervertalerQt(QMainWindow):
|
|
|
21249
21752
|
# Configure columns
|
|
21250
21753
|
self.table.setColumnCount(5)
|
|
21251
21754
|
self.table.setHorizontalHeaderLabels(["#", "Type", "Source", "Target", "Status"])
|
|
21252
|
-
|
|
21253
|
-
#
|
|
21755
|
+
|
|
21756
|
+
# Explicitly set header font to normal weight (not bold)
|
|
21254
21757
|
header = self.table.horizontalHeader()
|
|
21758
|
+
header_font = QFont(self.default_font_family, self.default_font_size, QFont.Weight.Normal)
|
|
21759
|
+
header.setFont(header_font)
|
|
21760
|
+
|
|
21761
|
+
# Also set font on individual header items through the model (extra insurance)
|
|
21762
|
+
model = self.table.model()
|
|
21763
|
+
if model:
|
|
21764
|
+
for col in range(5):
|
|
21765
|
+
item = model.headerData(col, Qt.Orientation.Horizontal, Qt.ItemDataRole.DisplayRole)
|
|
21766
|
+
model.setHeaderData(col, Qt.Orientation.Horizontal, item, Qt.ItemDataRole.DisplayRole)
|
|
21767
|
+
model.setHeaderData(col, Qt.Orientation.Horizontal, header_font, Qt.ItemDataRole.FontRole)
|
|
21768
|
+
|
|
21769
|
+
# Column widths - Source and Target columns stretch to fill space, others are interactive
|
|
21255
21770
|
header.setSectionResizeMode(0, QHeaderView.ResizeMode.Interactive) # ID
|
|
21256
21771
|
header.setSectionResizeMode(1, QHeaderView.ResizeMode.Interactive) # Type
|
|
21257
21772
|
header.setSectionResizeMode(2, QHeaderView.ResizeMode.Stretch) # Source - stretch to fill space
|
|
@@ -21265,7 +21780,7 @@ class SupervertalerQt(QMainWindow):
|
|
|
21265
21780
|
self.table.setColumnWidth(1, 40) # Type - narrower
|
|
21266
21781
|
self.table.setColumnWidth(2, 400) # Source
|
|
21267
21782
|
self.table.setColumnWidth(3, 400) # Target
|
|
21268
|
-
self.table.setColumnWidth(4,
|
|
21783
|
+
self.table.setColumnWidth(4, 50) # Status - compact width
|
|
21269
21784
|
|
|
21270
21785
|
# Enable word wrap in cells (both display and edit mode)
|
|
21271
21786
|
self.table.setWordWrap(True)
|
|
@@ -21295,6 +21810,51 @@ class SupervertalerQt(QMainWindow):
|
|
|
21295
21810
|
QTableWidget::item:last-child {
|
|
21296
21811
|
border-right: none;
|
|
21297
21812
|
}
|
|
21813
|
+
|
|
21814
|
+
/* Narrower scrollbar with visible arrow buttons */
|
|
21815
|
+
QScrollBar:vertical {
|
|
21816
|
+
border: none;
|
|
21817
|
+
background: #F0F0F0;
|
|
21818
|
+
width: 12px;
|
|
21819
|
+
margin: 12px 0 12px 0;
|
|
21820
|
+
}
|
|
21821
|
+
QScrollBar::handle:vertical {
|
|
21822
|
+
background: #C0C0C0;
|
|
21823
|
+
min-height: 20px;
|
|
21824
|
+
border-radius: 2px;
|
|
21825
|
+
}
|
|
21826
|
+
QScrollBar::handle:vertical:hover {
|
|
21827
|
+
background: #A0A0A0;
|
|
21828
|
+
}
|
|
21829
|
+
QScrollBar::add-line:vertical {
|
|
21830
|
+
height: 12px;
|
|
21831
|
+
background: #E0E0E0;
|
|
21832
|
+
subcontrol-position: bottom;
|
|
21833
|
+
subcontrol-origin: margin;
|
|
21834
|
+
}
|
|
21835
|
+
QScrollBar::add-line:vertical:hover {
|
|
21836
|
+
background: #2196F3;
|
|
21837
|
+
}
|
|
21838
|
+
QScrollBar::sub-line:vertical {
|
|
21839
|
+
height: 12px;
|
|
21840
|
+
background: #E0E0E0;
|
|
21841
|
+
subcontrol-position: top;
|
|
21842
|
+
subcontrol-origin: margin;
|
|
21843
|
+
}
|
|
21844
|
+
QScrollBar::sub-line:vertical:hover {
|
|
21845
|
+
background: #2196F3;
|
|
21846
|
+
}
|
|
21847
|
+
/* Arrow images */
|
|
21848
|
+
QScrollBar::up-arrow:vertical {
|
|
21849
|
+
image: url(assets/scrollbar_up.png);
|
|
21850
|
+
width: 8px;
|
|
21851
|
+
height: 8px;
|
|
21852
|
+
}
|
|
21853
|
+
QScrollBar::down-arrow:vertical {
|
|
21854
|
+
image: url(assets/scrollbar_down.png);
|
|
21855
|
+
width: 8px;
|
|
21856
|
+
height: 8px;
|
|
21857
|
+
}
|
|
21298
21858
|
""")
|
|
21299
21859
|
|
|
21300
21860
|
# Simplified editing: Double-click only (no F2 key) - companion tool philosophy
|
|
@@ -21317,8 +21877,8 @@ class SupervertalerQt(QMainWindow):
|
|
|
21317
21877
|
# Debug: Confirm signal connections
|
|
21318
21878
|
self.log("🔌 Table signals connected: currentCellChanged, itemClicked, cellDoubleClicked, itemSelectionChanged")
|
|
21319
21879
|
|
|
21320
|
-
#
|
|
21321
|
-
self.add_precision_scroll_buttons()
|
|
21880
|
+
# Precision scroll buttons removed (user preference)
|
|
21881
|
+
# self.add_precision_scroll_buttons()
|
|
21322
21882
|
|
|
21323
21883
|
def add_precision_scroll_buttons(self):
|
|
21324
21884
|
"""Add precision scroll buttons at top/bottom of scrollbar (memoQ-style)"""
|
|
@@ -21544,10 +22104,10 @@ class SupervertalerQt(QMainWindow):
|
|
|
21544
22104
|
editor_widget.dictate_btn = dictate_btn
|
|
21545
22105
|
|
|
21546
22106
|
save_btn = QPushButton("💾 Save")
|
|
21547
|
-
save_btn.setStyleSheet("background-color: #4CAF50; color: white; font-weight: bold;")
|
|
22107
|
+
save_btn.setStyleSheet("background-color: #4CAF50; color: white; font-weight: bold; padding: 3px 5px;")
|
|
21548
22108
|
save_btn.clicked.connect(self.save_tab_segment)
|
|
21549
22109
|
save_next_btn = QPushButton("✓ Confirm && Next (Ctrl+Enter)")
|
|
21550
|
-
save_next_btn.setStyleSheet("background-color: #2196F3; color: white; font-weight: bold;")
|
|
22110
|
+
save_next_btn.setStyleSheet("background-color: #2196F3; color: white; font-weight: bold; padding: 3px 5px;")
|
|
21551
22111
|
save_next_btn.clicked.connect(self.confirm_selected_or_next)
|
|
21552
22112
|
|
|
21553
22113
|
button_layout.addWidget(copy_btn)
|
|
@@ -21891,6 +22451,10 @@ class SupervertalerQt(QMainWindow):
|
|
|
21891
22451
|
# Update UI
|
|
21892
22452
|
self.project_file_path = None
|
|
21893
22453
|
self.project_modified = True # Mark as modified since it hasn't been saved
|
|
22454
|
+
|
|
22455
|
+
# Store original segment order for "Document Order" sort reset
|
|
22456
|
+
self._original_segment_order = self.current_project.segments.copy()
|
|
22457
|
+
|
|
21894
22458
|
self.update_window_title()
|
|
21895
22459
|
self.load_segments_to_grid()
|
|
21896
22460
|
self.initialize_tm_database() # Initialize TM for this project
|
|
@@ -21919,16 +22483,16 @@ class SupervertalerQt(QMainWindow):
|
|
|
21919
22483
|
if hasattr(self.lookup_tab, '_ahk') and self.lookup_tab._ahk:
|
|
21920
22484
|
try:
|
|
21921
22485
|
self.lookup_tab._ahk.stop_hotkeys()
|
|
21922
|
-
print("[
|
|
22486
|
+
print("[SuperLookup] ahk library hotkeys stopped")
|
|
21923
22487
|
except Exception as e:
|
|
21924
|
-
print(f"[
|
|
22488
|
+
print(f"[SuperLookup] Error stopping ahk library: {e}")
|
|
21925
22489
|
|
|
21926
22490
|
# Terminate external AutoHotkey process if running (fallback method)
|
|
21927
22491
|
if hasattr(self, 'lookup_tab') and hasattr(self.lookup_tab, 'ahk_process') and self.lookup_tab.ahk_process:
|
|
21928
22492
|
try:
|
|
21929
22493
|
self.lookup_tab.ahk_process.terminate()
|
|
21930
22494
|
self.lookup_tab.ahk_process.wait(timeout=2)
|
|
21931
|
-
print("[
|
|
22495
|
+
print("[SuperLookup] AHK process terminated")
|
|
21932
22496
|
except:
|
|
21933
22497
|
# Force kill if terminate doesn't work
|
|
21934
22498
|
try:
|
|
@@ -21936,7 +22500,7 @@ class SupervertalerQt(QMainWindow):
|
|
|
21936
22500
|
except:
|
|
21937
22501
|
pass
|
|
21938
22502
|
except Exception as e:
|
|
21939
|
-
print(f"[
|
|
22503
|
+
print(f"[SuperLookup] Error terminating AHK: {e}")
|
|
21940
22504
|
|
|
21941
22505
|
# Accept the close event
|
|
21942
22506
|
event.accept()
|
|
@@ -21972,6 +22536,12 @@ class SupervertalerQt(QMainWindow):
|
|
|
21972
22536
|
self.project_file_path = file_path
|
|
21973
22537
|
self.project_modified = False
|
|
21974
22538
|
|
|
22539
|
+
# Store original segment order for "Document Order" sort reset
|
|
22540
|
+
self._original_segment_order = self.current_project.segments.copy()
|
|
22541
|
+
|
|
22542
|
+
# Always reset sort state when loading - project should open in document order
|
|
22543
|
+
self.current_sort = None
|
|
22544
|
+
|
|
21975
22545
|
# Sync global language settings with project languages
|
|
21976
22546
|
if self.current_project.source_lang:
|
|
21977
22547
|
self.source_language = self.current_project.source_lang
|
|
@@ -23323,9 +23893,21 @@ class SupervertalerQt(QMainWindow):
|
|
|
23323
23893
|
original_path = getattr(self, 'original_docx', None) or getattr(self, 'current_document_path', None)
|
|
23324
23894
|
if original_path and os.path.exists(original_path):
|
|
23325
23895
|
self.current_project.original_docx_path = original_path
|
|
23326
|
-
|
|
23896
|
+
|
|
23897
|
+
# IMPORTANT: Always save segments in original document order, not sorted order
|
|
23898
|
+
# Store current sort state and temporarily restore original order
|
|
23899
|
+
current_sort_state = getattr(self, 'current_sort', None)
|
|
23900
|
+
current_segments = self.current_project.segments.copy() # Save current (possibly sorted) order
|
|
23901
|
+
|
|
23902
|
+
# Restore original order for saving
|
|
23903
|
+
if hasattr(self, '_original_segment_order') and self._original_segment_order:
|
|
23904
|
+
self.current_project.segments = self._original_segment_order.copy()
|
|
23905
|
+
|
|
23327
23906
|
with open(file_path, 'w', encoding='utf-8') as f:
|
|
23328
23907
|
json.dump(self.current_project.to_dict(), f, indent=2, ensure_ascii=False)
|
|
23908
|
+
|
|
23909
|
+
# Restore the current (sorted) order after saving
|
|
23910
|
+
self.current_project.segments = current_segments
|
|
23329
23911
|
|
|
23330
23912
|
self.project_modified = False
|
|
23331
23913
|
self.update_window_title()
|
|
@@ -29040,7 +29622,11 @@ class SupervertalerQt(QMainWindow):
|
|
|
29040
29622
|
def load_segments_to_grid(self):
|
|
29041
29623
|
"""Load segments into the grid with termbase highlighting"""
|
|
29042
29624
|
self.log(f"🔄🔄🔄 load_segments_to_grid CALLED - this will RELOAD grid from segment data!")
|
|
29043
|
-
|
|
29625
|
+
|
|
29626
|
+
# Ensure original segment order is stored (for Document Order sort)
|
|
29627
|
+
if self.current_project and not hasattr(self, '_original_segment_order'):
|
|
29628
|
+
self._original_segment_order = self.current_project.segments.copy()
|
|
29629
|
+
|
|
29044
29630
|
# Clear row color settings cache to ensure fresh settings are loaded
|
|
29045
29631
|
if hasattr(self, '_row_color_settings_cached'):
|
|
29046
29632
|
delattr(self, '_row_color_settings_cached')
|
|
@@ -29117,7 +29703,7 @@ class SupervertalerQt(QMainWindow):
|
|
|
29117
29703
|
id_item.setForeground(QColor(segment_num_color))
|
|
29118
29704
|
id_item.setBackground(QColor()) # Default background from theme
|
|
29119
29705
|
# Smaller font for segment numbers
|
|
29120
|
-
seg_num_font = QFont(self.default_font_family, max(
|
|
29706
|
+
seg_num_font = QFont(self.default_font_family, max(9, self.default_font_size - 1))
|
|
29121
29707
|
id_item.setFont(seg_num_font)
|
|
29122
29708
|
self.table.setItem(row, 0, id_item)
|
|
29123
29709
|
|
|
@@ -29209,7 +29795,7 @@ class SupervertalerQt(QMainWindow):
|
|
|
29209
29795
|
type_item.setForeground(QColor("#388E3C")) # Green for list items (works in both themes)
|
|
29210
29796
|
|
|
29211
29797
|
# Smaller font for type symbols
|
|
29212
|
-
type_font = QFont(self.default_font_family, max(
|
|
29798
|
+
type_font = QFont(self.default_font_family, max(9, self.default_font_size - 1))
|
|
29213
29799
|
type_item.setFont(type_font)
|
|
29214
29800
|
|
|
29215
29801
|
self.table.setItem(row, 1, type_item)
|
|
@@ -29659,7 +30245,10 @@ class SupervertalerQt(QMainWindow):
|
|
|
29659
30245
|
if shortcut_badge_tooltip:
|
|
29660
30246
|
badge_label.setToolTip(f"Press {shortcut_badge_tooltip} to insert")
|
|
29661
30247
|
header_layout.addWidget(badge_label)
|
|
29662
|
-
|
|
30248
|
+
|
|
30249
|
+
# Add stretch to push navigation controls to the right (aligned with scrollbar)
|
|
30250
|
+
header_layout.addStretch()
|
|
30251
|
+
|
|
29663
30252
|
nav_label = None
|
|
29664
30253
|
nav_buttons = None
|
|
29665
30254
|
|
|
@@ -29725,8 +30314,7 @@ class SupervertalerQt(QMainWindow):
|
|
|
29725
30314
|
self.theme_aware_arrows.extend([prev_btn, next_btn])
|
|
29726
30315
|
|
|
29727
30316
|
nav_buttons = [prev_btn, next_btn]
|
|
29728
|
-
|
|
29729
|
-
header_layout.addStretch()
|
|
30317
|
+
|
|
29730
30318
|
main_layout.addLayout(header_layout)
|
|
29731
30319
|
|
|
29732
30320
|
# Text area
|
|
@@ -30649,19 +31237,24 @@ class SupervertalerQt(QMainWindow):
|
|
|
30649
31237
|
widget.setToolTip(f"Notes: {segment.notes.strip()}")
|
|
30650
31238
|
|
|
30651
31239
|
status_def = get_status(segment.status)
|
|
30652
|
-
|
|
31240
|
+
|
|
31241
|
+
# Status icons: ✔ (green) and ❌ (naturally red emoji)
|
|
31242
|
+
icon_text = status_def.icon
|
|
31243
|
+
if segment.status == "confirmed":
|
|
31244
|
+
icon_html = f'<font color="#2e7d32" size="2">{icon_text}</font>' # Green checkmark
|
|
31245
|
+
else:
|
|
31246
|
+
icon_html = f'<font size="2">{icon_text}</font>' # Other icons (including ❌ emoji)
|
|
31247
|
+
|
|
31248
|
+
status_label = QLabel(icon_html)
|
|
31249
|
+
status_label.setTextFormat(Qt.TextFormat.RichText) # Enable HTML rendering
|
|
30653
31250
|
status_label.setAlignment(Qt.AlignmentFlag.AlignVCenter | Qt.AlignmentFlag.AlignLeft)
|
|
31251
|
+
status_label.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Expanding) # Expand vertically to match row height
|
|
30654
31252
|
status_label.setToolTip(status_def.label)
|
|
30655
|
-
|
|
30656
|
-
|
|
30657
|
-
# Make confirmed checkmark green
|
|
30658
|
-
color = "color: #2e7d32;" if segment.status == "confirmed" else ""
|
|
30659
|
-
|
|
30660
|
-
# Apply orange background highlight to status icon if segment has notes
|
|
31253
|
+
|
|
31254
|
+
# Add orange background if segment has notes (using stylesheet for background only)
|
|
30661
31255
|
if has_notes:
|
|
30662
|
-
status_label.setStyleSheet(
|
|
30663
|
-
|
|
30664
|
-
status_label.setStyleSheet(f"font-size: {font_size}; {color} padding-right: 4px;")
|
|
31256
|
+
status_label.setStyleSheet("padding: 2px 4px; background-color: rgba(255, 152, 0, 0.35); border-radius: 3px;")
|
|
31257
|
+
# Note: No stylesheet for non-notes case to avoid interfering with HTML color
|
|
30665
31258
|
layout.addWidget(status_label)
|
|
30666
31259
|
|
|
30667
31260
|
# Only add match label if there's a match percentage
|
|
@@ -30773,14 +31366,17 @@ class SupervertalerQt(QMainWindow):
|
|
|
30773
31366
|
"""Auto-resize all rows to fit content - Compact version"""
|
|
30774
31367
|
if not hasattr(self, 'table') or not self.table:
|
|
30775
31368
|
return
|
|
30776
|
-
|
|
31369
|
+
|
|
30777
31370
|
# Reduce width slightly to account for padding and prevent text cut-off
|
|
30778
31371
|
width_reduction = 8
|
|
30779
|
-
|
|
31372
|
+
|
|
30780
31373
|
# Manually calculate and set row heights for compact display
|
|
30781
31374
|
for row in range(self.table.rowCount()):
|
|
31375
|
+
# Keep UI responsive during large grid updates
|
|
31376
|
+
if row % 50 == 0:
|
|
31377
|
+
QApplication.processEvents()
|
|
30782
31378
|
self._auto_resize_single_row(row, width_reduction)
|
|
30783
|
-
|
|
31379
|
+
|
|
30784
31380
|
self.log("✓ Auto-resized rows to fit content (compact)")
|
|
30785
31381
|
self._enforce_status_row_heights()
|
|
30786
31382
|
|
|
@@ -30827,26 +31423,30 @@ class SupervertalerQt(QMainWindow):
|
|
|
30827
31423
|
def apply_font_to_grid(self):
|
|
30828
31424
|
"""Apply selected font to all grid cells"""
|
|
30829
31425
|
font = QFont(self.default_font_family, self.default_font_size)
|
|
30830
|
-
|
|
31426
|
+
|
|
30831
31427
|
self.table.setFont(font)
|
|
30832
|
-
|
|
30833
|
-
# Also update header font - same size as grid content,
|
|
30834
|
-
header_font = QFont(self.default_font_family, self.default_font_size, QFont.Weight.
|
|
31428
|
+
|
|
31429
|
+
# Also update header font - same size as grid content, normal weight
|
|
31430
|
+
header_font = QFont(self.default_font_family, self.default_font_size, QFont.Weight.Normal)
|
|
30835
31431
|
self.table.horizontalHeader().setFont(header_font)
|
|
30836
|
-
|
|
31432
|
+
|
|
30837
31433
|
# Update fonts in QTextEdit widgets (source and target columns)
|
|
30838
31434
|
if hasattr(self, 'table') and self.table:
|
|
30839
31435
|
for row in range(self.table.rowCount()):
|
|
31436
|
+
# Keep UI responsive during large grid updates
|
|
31437
|
+
if row % 50 == 0:
|
|
31438
|
+
QApplication.processEvents()
|
|
31439
|
+
|
|
30840
31440
|
# Source column (2) - ReadOnlyGridTextEditor
|
|
30841
31441
|
source_widget = self.table.cellWidget(row, 2)
|
|
30842
31442
|
if source_widget and isinstance(source_widget, ReadOnlyGridTextEditor):
|
|
30843
31443
|
source_widget.setFont(font)
|
|
30844
|
-
|
|
31444
|
+
|
|
30845
31445
|
# Target column (3) - EditableGridTextEditor
|
|
30846
31446
|
target_widget = self.table.cellWidget(row, 3)
|
|
30847
31447
|
if target_widget and isinstance(target_widget, EditableGridTextEditor):
|
|
30848
31448
|
target_widget.setFont(font)
|
|
30849
|
-
|
|
31449
|
+
|
|
30850
31450
|
# Adjust segment number column width based on font size
|
|
30851
31451
|
self._update_segment_column_width()
|
|
30852
31452
|
|
|
@@ -30870,12 +31470,12 @@ class SupervertalerQt(QMainWindow):
|
|
|
30870
31470
|
# Measure the width of the largest number (as string)
|
|
30871
31471
|
text_width = fm.horizontalAdvance(str(max_segment))
|
|
30872
31472
|
|
|
30873
|
-
# Add padding (
|
|
30874
|
-
new_width = text_width +
|
|
30875
|
-
|
|
30876
|
-
# Ensure minimum width for very small numbers
|
|
30877
|
-
new_width = max(30, new_width)
|
|
30878
|
-
|
|
31473
|
+
# Add padding (6px on each side = 12px total)
|
|
31474
|
+
new_width = text_width + 12
|
|
31475
|
+
|
|
31476
|
+
# Ensure minimum width for very small numbers, cap at width for ~1000
|
|
31477
|
+
new_width = max(30, min(new_width, 55))
|
|
31478
|
+
|
|
30879
31479
|
self.table.setColumnWidth(0, new_width)
|
|
30880
31480
|
|
|
30881
31481
|
def set_font_family(self, family_name: str):
|
|
@@ -30907,14 +31507,18 @@ class SupervertalerQt(QMainWindow):
|
|
|
30907
31507
|
"""Refresh tag highlight colors in all grid cells"""
|
|
30908
31508
|
if not hasattr(self, 'table') or not self.table:
|
|
30909
31509
|
return
|
|
30910
|
-
|
|
31510
|
+
|
|
30911
31511
|
for row in range(self.table.rowCount()):
|
|
31512
|
+
# Keep UI responsive during large grid updates
|
|
31513
|
+
if row % 50 == 0:
|
|
31514
|
+
QApplication.processEvents()
|
|
31515
|
+
|
|
30912
31516
|
# Source column (2) - ReadOnlyGridTextEditor
|
|
30913
31517
|
source_widget = self.table.cellWidget(row, 2)
|
|
30914
31518
|
if source_widget and isinstance(source_widget, ReadOnlyGridTextEditor):
|
|
30915
31519
|
if hasattr(source_widget, 'highlighter'):
|
|
30916
31520
|
source_widget.highlighter.set_tag_color(EditableGridTextEditor.tag_highlight_color)
|
|
30917
|
-
|
|
31521
|
+
|
|
30918
31522
|
# Target column (3) - EditableGridTextEditor
|
|
30919
31523
|
target_widget = self.table.cellWidget(row, 3)
|
|
30920
31524
|
if target_widget and isinstance(target_widget, EditableGridTextEditor):
|
|
@@ -30979,18 +31583,22 @@ class SupervertalerQt(QMainWindow):
|
|
|
30979
31583
|
"""Apply alternating row colors to all source and target cells in the grid"""
|
|
30980
31584
|
if not hasattr(self, 'table') or not self.table:
|
|
30981
31585
|
return
|
|
30982
|
-
|
|
31586
|
+
|
|
30983
31587
|
# Clear cached settings to force reload
|
|
30984
31588
|
if hasattr(self, '_row_color_settings_cached'):
|
|
30985
31589
|
delattr(self, '_row_color_settings_cached')
|
|
30986
|
-
|
|
31590
|
+
|
|
30987
31591
|
for row in range(self.table.rowCount()):
|
|
31592
|
+
# Keep UI responsive during large grid updates
|
|
31593
|
+
if row % 50 == 0:
|
|
31594
|
+
QApplication.processEvents()
|
|
31595
|
+
|
|
30988
31596
|
source_widget = self.table.cellWidget(row, 2)
|
|
30989
31597
|
target_widget = self.table.cellWidget(row, 3)
|
|
30990
|
-
|
|
31598
|
+
|
|
30991
31599
|
if source_widget and target_widget:
|
|
30992
31600
|
self._apply_row_color(row, source_widget, target_widget)
|
|
30993
|
-
|
|
31601
|
+
|
|
30994
31602
|
self.log("✓ Alternating row colors applied")
|
|
30995
31603
|
|
|
30996
31604
|
def on_font_changed(self):
|
|
@@ -36864,9 +37472,9 @@ OUTPUT ONLY THE SEGMENT MARKERS. DO NOT ADD EXPLANATIONS BEFORE OR AFTER."""
|
|
|
36864
37472
|
"""Update spellcheck button style based on enabled state"""
|
|
36865
37473
|
if hasattr(self, 'spellcheck_btn'):
|
|
36866
37474
|
if self.spellcheck_enabled:
|
|
36867
|
-
self.spellcheck_btn.setStyleSheet("background-color: #4CAF50; color: white; font-weight: bold;")
|
|
37475
|
+
self.spellcheck_btn.setStyleSheet("background-color: #4CAF50; color: white; font-weight: bold; padding: 3px 5px;")
|
|
36868
37476
|
else:
|
|
36869
|
-
self.spellcheck_btn.setStyleSheet("background-color: #9E9E9E; color: white; font-weight: bold;")
|
|
37477
|
+
self.spellcheck_btn.setStyleSheet("background-color: #9E9E9E; color: white; font-weight: bold; padding: 3px 5px;")
|
|
36870
37478
|
|
|
36871
37479
|
def _toggle_spellcheck(self, checked=None):
|
|
36872
37480
|
"""Toggle spellcheck on/off"""
|
|
@@ -37566,7 +38174,154 @@ OUTPUT ONLY THE SEGMENT MARKERS. DO NOT ADD EXPLANATIONS BEFORE OR AFTER."""
|
|
|
37566
38174
|
self.table.setUpdatesEnabled(True)
|
|
37567
38175
|
|
|
37568
38176
|
self.log(f"🔍 Advanced filters: showing {visible_count} of {len(self.current_project.segments)} segments")
|
|
37569
|
-
|
|
38177
|
+
|
|
38178
|
+
def apply_sort(self, sort_type: str = None):
|
|
38179
|
+
"""Sort segments by various criteria (similar to memoQ)"""
|
|
38180
|
+
if not self.current_project or not hasattr(self, 'table') or self.table is None:
|
|
38181
|
+
return
|
|
38182
|
+
|
|
38183
|
+
if not self.current_project.segments:
|
|
38184
|
+
return
|
|
38185
|
+
|
|
38186
|
+
# Show progress dialog during sorting
|
|
38187
|
+
from PyQt6.QtWidgets import QProgressDialog
|
|
38188
|
+
from PyQt6.QtCore import Qt
|
|
38189
|
+
|
|
38190
|
+
progress = QProgressDialog("Sorting segments, please wait...", None, 0, 0, self)
|
|
38191
|
+
progress.setWindowTitle("Sorting")
|
|
38192
|
+
progress.setWindowModality(Qt.WindowModality.WindowModal)
|
|
38193
|
+
progress.setMinimumDuration(0) # Show immediately
|
|
38194
|
+
progress.show()
|
|
38195
|
+
QApplication.processEvents() # Force UI update
|
|
38196
|
+
|
|
38197
|
+
try:
|
|
38198
|
+
# Store original document order if not already stored
|
|
38199
|
+
if not hasattr(self, '_original_segment_order'):
|
|
38200
|
+
self._original_segment_order = self.current_project.segments.copy()
|
|
38201
|
+
|
|
38202
|
+
# Update current sort state
|
|
38203
|
+
self.current_sort = sort_type
|
|
38204
|
+
|
|
38205
|
+
# If sort_type is None, restore document order
|
|
38206
|
+
if sort_type is None:
|
|
38207
|
+
# Restore document order by sorting by segment ID (original position)
|
|
38208
|
+
# This works even if the stored original order is wrong
|
|
38209
|
+
self.current_project.segments.sort(key=lambda seg: int(seg.id))
|
|
38210
|
+
|
|
38211
|
+
# Update stored original order to this correct order
|
|
38212
|
+
self._original_segment_order = self.current_project.segments.copy()
|
|
38213
|
+
|
|
38214
|
+
# Set pagination to "All" to show all segments
|
|
38215
|
+
if hasattr(self, 'page_size_combo') and self._widget_is_alive(self.page_size_combo):
|
|
38216
|
+
self.page_size_combo.blockSignals(True)
|
|
38217
|
+
self.page_size_combo.setCurrentText("All")
|
|
38218
|
+
self.page_size_combo.blockSignals(False)
|
|
38219
|
+
# Update the internal page size variable
|
|
38220
|
+
if hasattr(self, 'grid_page_size'):
|
|
38221
|
+
self.grid_page_size = 999999
|
|
38222
|
+
|
|
38223
|
+
self.load_segments_to_grid()
|
|
38224
|
+
self.log("↩️ Restored document order (showing all segments)")
|
|
38225
|
+
return
|
|
38226
|
+
|
|
38227
|
+
# Helper function to get text without tags for more accurate sorting
|
|
38228
|
+
def strip_tags(text: str) -> str:
|
|
38229
|
+
"""Remove HTML/XML tags from text for sorting"""
|
|
38230
|
+
import re
|
|
38231
|
+
return re.sub(r'<[^>]+>', '', text).strip()
|
|
38232
|
+
|
|
38233
|
+
# Calculate frequency maps if needed
|
|
38234
|
+
frequency_cache = {}
|
|
38235
|
+
if 'freq' in sort_type:
|
|
38236
|
+
from collections import Counter
|
|
38237
|
+
if 'source' in sort_type:
|
|
38238
|
+
counter = Counter(strip_tags(seg.source).lower() for seg in self.current_project.segments)
|
|
38239
|
+
frequency_cache = {strip_tags(seg.source).lower(): counter[strip_tags(seg.source).lower()]
|
|
38240
|
+
for seg in self.current_project.segments}
|
|
38241
|
+
else: # target frequency
|
|
38242
|
+
counter = Counter(strip_tags(seg.target).lower() for seg in self.current_project.segments if seg.target)
|
|
38243
|
+
frequency_cache = {strip_tags(seg.target).lower(): counter[strip_tags(seg.target).lower()]
|
|
38244
|
+
for seg in self.current_project.segments if seg.target}
|
|
38245
|
+
|
|
38246
|
+
# Sort based on selected criterion
|
|
38247
|
+
if sort_type == 'source_asc':
|
|
38248
|
+
self.current_project.segments.sort(key=lambda s: strip_tags(s.source).lower())
|
|
38249
|
+
sort_name = "Source A → Z"
|
|
38250
|
+
elif sort_type == 'source_desc':
|
|
38251
|
+
self.current_project.segments.sort(key=lambda s: strip_tags(s.source).lower(), reverse=True)
|
|
38252
|
+
sort_name = "Source Z → A"
|
|
38253
|
+
elif sort_type == 'target_asc':
|
|
38254
|
+
self.current_project.segments.sort(key=lambda s: strip_tags(s.target).lower() if s.target else "")
|
|
38255
|
+
sort_name = "Target A → Z"
|
|
38256
|
+
elif sort_type == 'target_desc':
|
|
38257
|
+
self.current_project.segments.sort(key=lambda s: strip_tags(s.target).lower() if s.target else "", reverse=True)
|
|
38258
|
+
sort_name = "Target Z → A"
|
|
38259
|
+
elif sort_type == 'source_length_asc':
|
|
38260
|
+
self.current_project.segments.sort(key=lambda s: len(strip_tags(s.source)))
|
|
38261
|
+
sort_name = "Source (shorter first)"
|
|
38262
|
+
elif sort_type == 'source_length_desc':
|
|
38263
|
+
self.current_project.segments.sort(key=lambda s: len(strip_tags(s.source)), reverse=True)
|
|
38264
|
+
sort_name = "Source (longer first)"
|
|
38265
|
+
elif sort_type == 'target_length_asc':
|
|
38266
|
+
self.current_project.segments.sort(key=lambda s: len(strip_tags(s.target)) if s.target else 0)
|
|
38267
|
+
sort_name = "Target (shorter first)"
|
|
38268
|
+
elif sort_type == 'target_length_desc':
|
|
38269
|
+
self.current_project.segments.sort(key=lambda s: len(strip_tags(s.target)) if s.target else 0, reverse=True)
|
|
38270
|
+
sort_name = "Target (longer first)"
|
|
38271
|
+
elif sort_type == 'match_asc':
|
|
38272
|
+
self.current_project.segments.sort(key=lambda s: getattr(s, 'match_percent', 0) or 0)
|
|
38273
|
+
sort_name = "Match Rate (lower first)"
|
|
38274
|
+
elif sort_type == 'match_desc':
|
|
38275
|
+
self.current_project.segments.sort(key=lambda s: getattr(s, 'match_percent', 0) or 0, reverse=True)
|
|
38276
|
+
sort_name = "Match Rate (higher first)"
|
|
38277
|
+
elif sort_type == 'source_freq_asc':
|
|
38278
|
+
self.current_project.segments.sort(key=lambda s: frequency_cache.get(strip_tags(s.source).lower(), 0))
|
|
38279
|
+
sort_name = "Source Frequency (lower first)"
|
|
38280
|
+
elif sort_type == 'source_freq_desc':
|
|
38281
|
+
self.current_project.segments.sort(key=lambda s: frequency_cache.get(strip_tags(s.source).lower(), 0), reverse=True)
|
|
38282
|
+
sort_name = "Source Frequency (higher first)"
|
|
38283
|
+
elif sort_type == 'target_freq_asc':
|
|
38284
|
+
self.current_project.segments.sort(key=lambda s: frequency_cache.get(strip_tags(s.target).lower(), 0) if s.target else 0)
|
|
38285
|
+
sort_name = "Target Frequency (lower first)"
|
|
38286
|
+
elif sort_type == 'target_freq_desc':
|
|
38287
|
+
self.current_project.segments.sort(key=lambda s: frequency_cache.get(strip_tags(s.target).lower(), 0) if s.target else 0, reverse=True)
|
|
38288
|
+
sort_name = "Target Frequency (higher first)"
|
|
38289
|
+
elif sort_type == 'modified_asc':
|
|
38290
|
+
self.current_project.segments.sort(key=lambda s: s.modified_at if s.modified_at else "")
|
|
38291
|
+
sort_name = "Last Changed (oldest first)"
|
|
38292
|
+
elif sort_type == 'modified_desc':
|
|
38293
|
+
self.current_project.segments.sort(key=lambda s: s.modified_at if s.modified_at else "", reverse=True)
|
|
38294
|
+
sort_name = "Last Changed (newest first)"
|
|
38295
|
+
elif sort_type == 'status':
|
|
38296
|
+
# Sort by status in a logical order: not_started, draft, translated, confirmed
|
|
38297
|
+
status_order = {'not_started': 0, 'draft': 1, 'translated': 2, 'confirmed': 3}
|
|
38298
|
+
self.current_project.segments.sort(key=lambda s: status_order.get(s.status, 99))
|
|
38299
|
+
sort_name = "Row Status"
|
|
38300
|
+
else:
|
|
38301
|
+
self.log(f"⚠️ Unknown sort type: {sort_type}")
|
|
38302
|
+
return
|
|
38303
|
+
|
|
38304
|
+
# Set pagination to "All" to show all sorted segments
|
|
38305
|
+
if hasattr(self, 'page_size_combo') and self._widget_is_alive(self.page_size_combo):
|
|
38306
|
+
self.page_size_combo.blockSignals(True)
|
|
38307
|
+
self.page_size_combo.setCurrentText("All")
|
|
38308
|
+
self.page_size_combo.blockSignals(False)
|
|
38309
|
+
# Update the internal page size variable
|
|
38310
|
+
if hasattr(self, 'grid_page_size'):
|
|
38311
|
+
self.grid_page_size = 999999
|
|
38312
|
+
|
|
38313
|
+
# Reload grid to reflect new order
|
|
38314
|
+
self.load_segments_to_grid()
|
|
38315
|
+
self.log(f"⇅ Sorted by: {sort_name} (showing all segments)")
|
|
38316
|
+
|
|
38317
|
+
except Exception as e:
|
|
38318
|
+
self.log(f"❌ Error sorting segments: {e}")
|
|
38319
|
+
import traceback
|
|
38320
|
+
traceback.print_exc()
|
|
38321
|
+
finally:
|
|
38322
|
+
# Close progress dialog
|
|
38323
|
+
progress.close()
|
|
38324
|
+
|
|
37570
38325
|
# ========================================================================
|
|
37571
38326
|
# TABBED SEGMENT EDITOR METHODS (for Grid view)
|
|
37572
38327
|
# ========================================================================
|
|
@@ -39560,32 +40315,30 @@ OUTPUT ONLY THE SEGMENT MARKERS. DO NOT ADD EXPLANATIONS BEFORE OR AFTER."""
|
|
|
39560
40315
|
|
|
39561
40316
|
def _toggle_tag_view_via_shortcut(self):
|
|
39562
40317
|
"""Toggle tag view using keyboard shortcut (Ctrl+Alt+T)"""
|
|
39563
|
-
if hasattr(self, '
|
|
39564
|
-
# Toggle the
|
|
39565
|
-
new_state = not self.
|
|
39566
|
-
self.
|
|
39567
|
-
self.toggle_tag_view(new_state, self.tag_view_btn)
|
|
40318
|
+
if hasattr(self, 'wysiwyg_btn') and hasattr(self, 'tags_btn'):
|
|
40319
|
+
# Toggle between the two modes
|
|
40320
|
+
new_state = not self.show_tags
|
|
40321
|
+
self.toggle_tag_view(new_state, None)
|
|
39568
40322
|
|
|
39569
40323
|
def _enable_tag_view_after_import(self):
|
|
39570
40324
|
"""Auto-enable Tag View after importing a document with formatting tags"""
|
|
39571
|
-
if hasattr(self, '
|
|
39572
|
-
self.
|
|
39573
|
-
self.toggle_tag_view(True, self.tag_view_btn)
|
|
40325
|
+
if hasattr(self, 'tags_btn'):
|
|
40326
|
+
self.toggle_tag_view(True, None)
|
|
39574
40327
|
self.log("🏷️ Tag View auto-enabled (formatting tags detected in import)")
|
|
39575
40328
|
|
|
39576
40329
|
def toggle_tag_view(self, checked: bool, button: QPushButton = None):
|
|
39577
40330
|
"""Toggle between Tag View (showing raw tags) and WYSIWYG View (formatted display)"""
|
|
39578
40331
|
self.show_tags = checked
|
|
39579
|
-
|
|
39580
|
-
# Update
|
|
39581
|
-
if
|
|
40332
|
+
|
|
40333
|
+
# Update segmented control buttons if they exist
|
|
40334
|
+
if hasattr(self, 'wysiwyg_btn') and hasattr(self, 'tags_btn'):
|
|
39582
40335
|
if checked:
|
|
39583
|
-
|
|
40336
|
+
self.tags_btn.setChecked(True)
|
|
39584
40337
|
else:
|
|
39585
|
-
|
|
39586
|
-
|
|
40338
|
+
self.wysiwyg_btn.setChecked(True)
|
|
40339
|
+
|
|
39587
40340
|
self.log(f"{'🏷️ Tag View ENABLED - showing raw tags' if checked else '✨ WYSIWYG View ENABLED - showing formatted text'}")
|
|
39588
|
-
|
|
40341
|
+
|
|
39589
40342
|
# Refresh the grid to update display
|
|
39590
40343
|
if hasattr(self, 'table') and self.current_project:
|
|
39591
40344
|
self._refresh_grid_display_mode()
|
|
@@ -40958,45 +41711,12 @@ OUTPUT ONLY THE SEGMENT MARKERS. DO NOT ADD EXPLANATIONS BEFORE OR AFTER."""
|
|
|
40958
41711
|
event.accept()
|
|
40959
41712
|
|
|
40960
41713
|
def _cleanup_web_views(self):
|
|
40961
|
-
"""Clean up WebEngine views to prevent
|
|
40962
|
-
|
|
40963
|
-
|
|
40964
|
-
|
|
40965
|
-
|
|
40966
|
-
|
|
40967
|
-
if hasattr(tab, 'web_view'):
|
|
40968
|
-
# Stop any loading/rendering
|
|
40969
|
-
tab.web_view.stop()
|
|
40970
|
-
# Clear page to release resources
|
|
40971
|
-
tab.web_view.setPage(None)
|
|
40972
|
-
tab.web_view.setUrl(QUrl('about:blank'))
|
|
40973
|
-
tab.web_view.deleteLater()
|
|
40974
|
-
except:
|
|
40975
|
-
pass
|
|
40976
|
-
|
|
40977
|
-
# Close Superlookup web views (from Web Resources tab)
|
|
40978
|
-
if hasattr(self, 'web_views'):
|
|
40979
|
-
for resource_id, web_view in list(self.web_views.items()):
|
|
40980
|
-
try:
|
|
40981
|
-
web_view.stop()
|
|
40982
|
-
web_view.setPage(None)
|
|
40983
|
-
web_view.setUrl(QUrl('about:blank'))
|
|
40984
|
-
web_view.deleteLater()
|
|
40985
|
-
except:
|
|
40986
|
-
pass
|
|
40987
|
-
self.web_views.clear()
|
|
40988
|
-
|
|
40989
|
-
# Process events multiple times to ensure cleanup completes
|
|
40990
|
-
from PyQt6.QtWidgets import QApplication
|
|
40991
|
-
from PyQt6.QtCore import QUrl
|
|
40992
|
-
for _ in range(3):
|
|
40993
|
-
QApplication.processEvents()
|
|
40994
|
-
|
|
40995
|
-
# Small delay to allow Qt to finish cleanup
|
|
40996
|
-
import time
|
|
40997
|
-
time.sleep(0.1)
|
|
40998
|
-
except:
|
|
40999
|
-
pass
|
|
41714
|
+
"""Clean up WebEngine views - DISABLED to prevent crash"""
|
|
41715
|
+
# WebEngine cleanup has been disabled because it was causing Python crashes
|
|
41716
|
+
# on program exit. Qt will handle WebEngine cleanup automatically, though
|
|
41717
|
+
# you may see a "Release of profile requested" warning which is harmless.
|
|
41718
|
+
print("[WebEngine Cleanup] Skipping manual cleanup - letting Qt handle it")
|
|
41719
|
+
pass
|
|
41000
41720
|
|
|
41001
41721
|
def _close_detached_log_windows(self):
|
|
41002
41722
|
"""Close all detached log windows when main window closes"""
|
|
@@ -43541,23 +44261,58 @@ OUTPUT ONLY THE SEGMENT MARKERS. DO NOT ADD EXPLANATIONS BEFORE OR AFTER."""
|
|
|
43541
44261
|
"""Call DeepL API"""
|
|
43542
44262
|
try:
|
|
43543
44263
|
import deepl
|
|
43544
|
-
|
|
44264
|
+
|
|
43545
44265
|
if not api_key:
|
|
43546
44266
|
api_keys = self.load_api_keys()
|
|
43547
44267
|
api_key = api_keys.get("deepl")
|
|
43548
|
-
|
|
44268
|
+
|
|
43549
44269
|
if not api_key:
|
|
43550
44270
|
return "[DeepL requires API key]"
|
|
43551
|
-
|
|
44271
|
+
|
|
43552
44272
|
translator = deepl.Translator(api_key)
|
|
43553
|
-
|
|
43554
|
-
# Convert language
|
|
44273
|
+
|
|
44274
|
+
# Convert source language code (DeepL uses uppercase, no variant needed for source)
|
|
43555
44275
|
src_code = source_lang.split('-')[0].split('_')[0].upper()
|
|
43556
|
-
|
|
43557
|
-
|
|
44276
|
+
|
|
44277
|
+
# Convert target language code - DeepL requires variants for some languages
|
|
44278
|
+
# Handle full codes like "en-US", "en-GB", "pt-BR", "pt-PT"
|
|
44279
|
+
tgt_upper = target_lang.upper().replace('_', '-')
|
|
44280
|
+
|
|
44281
|
+
# DeepL target language mapping - some require specific variants
|
|
44282
|
+
deepl_target_map = {
|
|
44283
|
+
# English variants (EN alone is deprecated)
|
|
44284
|
+
'EN': 'EN-US', # Default to US English
|
|
44285
|
+
'EN-US': 'EN-US',
|
|
44286
|
+
'EN-GB': 'EN-GB',
|
|
44287
|
+
'EN-AU': 'EN-GB', # Map Australian to British
|
|
44288
|
+
'EN-CA': 'EN-US', # Map Canadian to US
|
|
44289
|
+
# Portuguese variants
|
|
44290
|
+
'PT': 'PT-PT', # Default to European Portuguese
|
|
44291
|
+
'PT-PT': 'PT-PT',
|
|
44292
|
+
'PT-BR': 'PT-BR',
|
|
44293
|
+
# Chinese variants
|
|
44294
|
+
'ZH': 'ZH-HANS', # Default to Simplified
|
|
44295
|
+
'ZH-CN': 'ZH-HANS',
|
|
44296
|
+
'ZH-TW': 'ZH-HANT',
|
|
44297
|
+
'ZH-HANS': 'ZH-HANS',
|
|
44298
|
+
'ZH-HANT': 'ZH-HANT',
|
|
44299
|
+
}
|
|
44300
|
+
|
|
44301
|
+
# Check if full code matches first, then base code
|
|
44302
|
+
if tgt_upper in deepl_target_map:
|
|
44303
|
+
tgt_code = deepl_target_map[tgt_upper]
|
|
44304
|
+
else:
|
|
44305
|
+
# Extract base code and check
|
|
44306
|
+
base_code = tgt_upper.split('-')[0]
|
|
44307
|
+
if base_code in deepl_target_map:
|
|
44308
|
+
tgt_code = deepl_target_map[base_code]
|
|
44309
|
+
else:
|
|
44310
|
+
# Use base code as-is for other languages
|
|
44311
|
+
tgt_code = base_code
|
|
44312
|
+
|
|
43558
44313
|
result = translator.translate_text(text, source_lang=src_code, target_lang=tgt_code)
|
|
43559
44314
|
return result.text
|
|
43560
|
-
|
|
44315
|
+
|
|
43561
44316
|
except ImportError:
|
|
43562
44317
|
return "[DeepL requires: pip install deepl]"
|
|
43563
44318
|
except Exception as e:
|
|
@@ -44003,8 +44758,11 @@ OUTPUT ONLY THE SEGMENT MARKERS. DO NOT ADD EXPLANATIONS BEFORE OR AFTER."""
|
|
|
44003
44758
|
# Determine segment number color based on theme
|
|
44004
44759
|
is_dark_theme = theme.name == "Dark"
|
|
44005
44760
|
segment_num_color = theme.text if is_dark_theme else "black"
|
|
44006
|
-
|
|
44761
|
+
|
|
44007
44762
|
for row in range(self.table.rowCount()):
|
|
44763
|
+
# Keep UI responsive during large grid updates
|
|
44764
|
+
if row % 50 == 0:
|
|
44765
|
+
QApplication.processEvents()
|
|
44008
44766
|
id_item = self.table.item(row, 0)
|
|
44009
44767
|
if id_item:
|
|
44010
44768
|
# Don't change currently highlighted row (orange background)
|
|
@@ -45093,7 +45851,7 @@ class SuperlookupTab(QWidget):
|
|
|
45093
45851
|
self.main_window = parent # Store reference to main window for database access
|
|
45094
45852
|
self.user_data_path = user_data_path # Store user data path for web cache
|
|
45095
45853
|
|
|
45096
|
-
print("[
|
|
45854
|
+
print("[SuperLookup] SuperlookupTab.__init__ called")
|
|
45097
45855
|
|
|
45098
45856
|
# Get theme manager from main window (try parent first, then parent's parent for dialogs)
|
|
45099
45857
|
self.theme_manager = getattr(parent, 'theme_manager', None)
|
|
@@ -45102,16 +45860,16 @@ class SuperlookupTab(QWidget):
|
|
|
45102
45860
|
parent_parent = getattr(parent, 'parent', lambda: None)()
|
|
45103
45861
|
if parent_parent:
|
|
45104
45862
|
self.theme_manager = getattr(parent_parent, 'theme_manager', None)
|
|
45105
|
-
print(f"[
|
|
45863
|
+
print(f"[SuperLookup] theme_manager: {self.theme_manager is not None}")
|
|
45106
45864
|
|
|
45107
45865
|
# Import lookup engine
|
|
45108
45866
|
try:
|
|
45109
45867
|
from modules.superlookup import SuperlookupEngine, LookupResult
|
|
45110
45868
|
self.SuperlookupEngine = SuperlookupEngine
|
|
45111
45869
|
self.LookupResult = LookupResult
|
|
45112
|
-
print("[
|
|
45870
|
+
print("[SuperLookup] Successfully imported SuperlookupEngine")
|
|
45113
45871
|
except ImportError as e:
|
|
45114
|
-
print(f"[
|
|
45872
|
+
print(f"[SuperLookup] IMPORT ERROR: {e}")
|
|
45115
45873
|
QMessageBox.critical(
|
|
45116
45874
|
self,
|
|
45117
45875
|
"Missing Module",
|
|
@@ -45193,7 +45951,7 @@ class SuperlookupTab(QWidget):
|
|
|
45193
45951
|
if self.db_manager or self.termbase_mgr:
|
|
45194
45952
|
self.populate_language_dropdowns()
|
|
45195
45953
|
self._languages_populated = True
|
|
45196
|
-
print("[
|
|
45954
|
+
print("[SuperLookup] Languages populated on first show")
|
|
45197
45955
|
|
|
45198
45956
|
def init_ui(self):
|
|
45199
45957
|
"""Initialize the UI"""
|
|
@@ -45202,7 +45960,7 @@ class SuperlookupTab(QWidget):
|
|
|
45202
45960
|
layout.setSpacing(5) # Reduced from 10 to 5 for consistency
|
|
45203
45961
|
|
|
45204
45962
|
# Header
|
|
45205
|
-
header = QLabel("🔍
|
|
45963
|
+
header = QLabel("🔍 SuperLookup")
|
|
45206
45964
|
header.setStyleSheet("font-size: 16pt; font-weight: bold; color: #1976D2;")
|
|
45207
45965
|
layout.addWidget(header, 0) # 0 = no stretch, stays compact
|
|
45208
45966
|
|
|
@@ -45696,7 +46454,7 @@ class SuperlookupTab(QWidget):
|
|
|
45696
46454
|
})
|
|
45697
46455
|
|
|
45698
46456
|
except Exception as e:
|
|
45699
|
-
print(f"[
|
|
46457
|
+
print(f"[SuperLookup] MT error ({provider_name}): {e}")
|
|
45700
46458
|
results.append({
|
|
45701
46459
|
'provider': provider_name,
|
|
45702
46460
|
'translation': f"[Error: {str(e)}]",
|
|
@@ -45787,9 +46545,9 @@ class SuperlookupTab(QWidget):
|
|
|
45787
46545
|
self.web_profile = QWebEngineProfile("SuperlookupProfile", self)
|
|
45788
46546
|
self.web_profile.setPersistentStoragePath(storage_path)
|
|
45789
46547
|
self.web_profile.setPersistentCookiesPolicy(QWebEngineProfile.PersistentCookiesPolicy.ForcePersistentCookies)
|
|
45790
|
-
print(f"[
|
|
46548
|
+
print(f"[SuperLookup] QWebEngineView available - embedded browser enabled with persistent storage at {storage_path}")
|
|
45791
46549
|
except ImportError:
|
|
45792
|
-
print("[
|
|
46550
|
+
print("[SuperLookup] QWebEngineView not available - external browser only")
|
|
45793
46551
|
self.QWebEngineView = None
|
|
45794
46552
|
self.QWebEngineProfile = None
|
|
45795
46553
|
|
|
@@ -46241,7 +46999,7 @@ class SuperlookupTab(QWidget):
|
|
|
46241
46999
|
self.web_view_stack.addWidget(web_view)
|
|
46242
47000
|
self.web_views[resource['id']] = web_view
|
|
46243
47001
|
|
|
46244
|
-
print(f"[
|
|
47002
|
+
print(f"[SuperLookup] Created web view for {resource['name']} (lazy load)")
|
|
46245
47003
|
|
|
46246
47004
|
def _get_web_view_index(self, resource_id):
|
|
46247
47005
|
"""Get the stack index for a web view by resource ID"""
|
|
@@ -46258,7 +47016,7 @@ class SuperlookupTab(QWidget):
|
|
|
46258
47016
|
|
|
46259
47017
|
self._update_web_view_for_mode()
|
|
46260
47018
|
self._show_web_welcome_message()
|
|
46261
|
-
print(f"[
|
|
47019
|
+
print(f"[SuperLookup] Web browser mode changed to: {self.web_browser_mode}")
|
|
46262
47020
|
|
|
46263
47021
|
def _update_web_view_for_mode(self):
|
|
46264
47022
|
"""Update the view stack based on current mode"""
|
|
@@ -46891,11 +47649,11 @@ class SuperlookupTab(QWidget):
|
|
|
46891
47649
|
if index == 2:
|
|
46892
47650
|
# Initialize Supermemory when tab is first viewed
|
|
46893
47651
|
if not self.supermemory_engine:
|
|
46894
|
-
print("[
|
|
47652
|
+
print("[SuperLookup] Supermemory tab viewed - initializing engine")
|
|
46895
47653
|
self.init_supermemory()
|
|
46896
47654
|
# Settings tab is at index 5
|
|
46897
47655
|
elif index == 5:
|
|
46898
|
-
print("[
|
|
47656
|
+
print("[SuperLookup] Settings tab viewed - refreshing resource lists")
|
|
46899
47657
|
self.refresh_tm_list()
|
|
46900
47658
|
self.refresh_termbase_list()
|
|
46901
47659
|
self.populate_language_dropdowns()
|
|
@@ -46903,19 +47661,19 @@ class SuperlookupTab(QWidget):
|
|
|
46903
47661
|
def on_tm_search_toggled(self, state):
|
|
46904
47662
|
"""Handle TM search checkbox toggle"""
|
|
46905
47663
|
self.search_tm_enabled = (state == Qt.CheckState.Checked.value)
|
|
46906
|
-
print(f"[
|
|
47664
|
+
print(f"[SuperLookup] TM search {'enabled' if self.search_tm_enabled else 'disabled'}")
|
|
46907
47665
|
|
|
46908
47666
|
def on_termbase_search_toggled(self, state):
|
|
46909
47667
|
"""Handle termbase search checkbox toggle"""
|
|
46910
47668
|
self.search_termbase_enabled = (state == Qt.CheckState.Checked.value)
|
|
46911
|
-
print(f"[
|
|
47669
|
+
print(f"[SuperLookup] Termbase search {'enabled' if self.search_termbase_enabled else 'disabled'}")
|
|
46912
47670
|
|
|
46913
47671
|
def _on_web_resource_checkbox_changed(self, index: int, state: int):
|
|
46914
47672
|
"""Handle web resource checkbox change - show/hide corresponding sidebar button"""
|
|
46915
47673
|
is_checked = (state == Qt.CheckState.Checked.value)
|
|
46916
47674
|
if hasattr(self, 'web_resource_buttons') and index < len(self.web_resource_buttons):
|
|
46917
47675
|
self.web_resource_buttons[index].setVisible(is_checked)
|
|
46918
|
-
print(f"[
|
|
47676
|
+
print(f"[SuperLookup] Web resource {index} {'shown' if is_checked else 'hidden'}")
|
|
46919
47677
|
|
|
46920
47678
|
# If the hidden resource was selected, select the first visible one
|
|
46921
47679
|
if not is_checked and hasattr(self, 'current_web_resource_index') and self.current_web_resource_index == index:
|
|
@@ -46933,18 +47691,18 @@ class SuperlookupTab(QWidget):
|
|
|
46933
47691
|
checkbox.deleteLater()
|
|
46934
47692
|
self.tm_checkboxes.clear()
|
|
46935
47693
|
|
|
46936
|
-
print(f"[
|
|
46937
|
-
print(f"[
|
|
47694
|
+
print(f"[SuperLookup] refresh_tm_list called")
|
|
47695
|
+
print(f"[SuperLookup] main_window exists: {self.main_window is not None}")
|
|
46938
47696
|
|
|
46939
47697
|
# Get TMs from main window's database
|
|
46940
47698
|
if self.main_window and hasattr(self.main_window, 'db_manager') and self.main_window.db_manager:
|
|
46941
47699
|
try:
|
|
46942
|
-
print(f"[
|
|
47700
|
+
print(f"[SuperLookup] db_manager found, querying TMs...")
|
|
46943
47701
|
cursor = self.main_window.db_manager.cursor
|
|
46944
47702
|
cursor.execute("SELECT id, name, tm_id FROM translation_memories ORDER BY name")
|
|
46945
47703
|
tms = cursor.fetchall()
|
|
46946
47704
|
|
|
46947
|
-
print(f"[
|
|
47705
|
+
print(f"[SuperLookup] Query returned {len(tms)} TMs")
|
|
46948
47706
|
|
|
46949
47707
|
for db_id, tm_name, tm_id_str in tms:
|
|
46950
47708
|
checkbox = CheckmarkCheckBox(f"{tm_name} (ID: {db_id})")
|
|
@@ -46955,13 +47713,13 @@ class SuperlookupTab(QWidget):
|
|
|
46955
47713
|
# Insert before the stretch at the end
|
|
46956
47714
|
self.tm_scroll_layout.insertWidget(len(self.tm_checkboxes) - 1, checkbox)
|
|
46957
47715
|
|
|
46958
|
-
print(f"[
|
|
47716
|
+
print(f"[SuperLookup] ✓ Loaded {len(tms)} TMs")
|
|
46959
47717
|
except Exception as e:
|
|
46960
|
-
print(f"[
|
|
47718
|
+
print(f"[SuperLookup] ✗ Error loading TMs: {e}")
|
|
46961
47719
|
import traceback
|
|
46962
47720
|
traceback.print_exc()
|
|
46963
47721
|
else:
|
|
46964
|
-
print(f"[
|
|
47722
|
+
print(f"[SuperLookup] db_manager not available")
|
|
46965
47723
|
# Add placeholder label
|
|
46966
47724
|
placeholder = QLabel("No database connection - TMs unavailable")
|
|
46967
47725
|
placeholder.setStyleSheet("color: #999; font-style: italic;")
|
|
@@ -46975,16 +47733,16 @@ class SuperlookupTab(QWidget):
|
|
|
46975
47733
|
checkbox.deleteLater()
|
|
46976
47734
|
self.tb_checkboxes.clear()
|
|
46977
47735
|
|
|
46978
|
-
print(f"[
|
|
46979
|
-
print(f"[
|
|
47736
|
+
print(f"[SuperLookup] refresh_termbase_list called")
|
|
47737
|
+
print(f"[SuperLookup] main_window exists: {self.main_window is not None}")
|
|
46980
47738
|
|
|
46981
47739
|
# Try termbase_mgr first (preferred method)
|
|
46982
47740
|
if self.main_window and hasattr(self.main_window, 'termbase_mgr') and self.main_window.termbase_mgr:
|
|
46983
47741
|
try:
|
|
46984
|
-
print(f"[
|
|
47742
|
+
print(f"[SuperLookup] termbase_mgr found, querying termbases...")
|
|
46985
47743
|
termbases = self.main_window.termbase_mgr.get_all_termbases()
|
|
46986
47744
|
|
|
46987
|
-
print(f"[
|
|
47745
|
+
print(f"[SuperLookup] get_all_termbases() returned {len(termbases)} termbases")
|
|
46988
47746
|
|
|
46989
47747
|
for tb in termbases:
|
|
46990
47748
|
tb_id = tb.get('id')
|
|
@@ -46996,22 +47754,22 @@ class SuperlookupTab(QWidget):
|
|
|
46996
47754
|
# Insert before the stretch at the end
|
|
46997
47755
|
self.tb_scroll_layout.insertWidget(len(self.tb_checkboxes) - 1, checkbox)
|
|
46998
47756
|
|
|
46999
|
-
print(f"[
|
|
47757
|
+
print(f"[SuperLookup] ✓ Loaded {len(termbases)} termbases via termbase_mgr")
|
|
47000
47758
|
return
|
|
47001
47759
|
except Exception as e:
|
|
47002
|
-
print(f"[
|
|
47760
|
+
print(f"[SuperLookup] ✗ Error loading termbases via termbase_mgr: {e}")
|
|
47003
47761
|
import traceback
|
|
47004
47762
|
traceback.print_exc()
|
|
47005
47763
|
|
|
47006
47764
|
# Fallback to direct database query
|
|
47007
47765
|
if self.main_window and hasattr(self.main_window, 'db_manager') and self.main_window.db_manager:
|
|
47008
47766
|
try:
|
|
47009
|
-
print(f"[
|
|
47767
|
+
print(f"[SuperLookup] db_manager found, querying termbases...")
|
|
47010
47768
|
cursor = self.main_window.db_manager.cursor
|
|
47011
47769
|
cursor.execute("SELECT id, name FROM termbases ORDER BY name")
|
|
47012
47770
|
termbases = cursor.fetchall()
|
|
47013
47771
|
|
|
47014
|
-
print(f"[
|
|
47772
|
+
print(f"[SuperLookup] Query returned {len(termbases)} termbases")
|
|
47015
47773
|
|
|
47016
47774
|
for tb_id, tb_name in termbases:
|
|
47017
47775
|
checkbox = CheckmarkCheckBox(f"{tb_name} (ID: {tb_id})")
|
|
@@ -47021,13 +47779,13 @@ class SuperlookupTab(QWidget):
|
|
|
47021
47779
|
# Insert before the stretch at the end
|
|
47022
47780
|
self.tb_scroll_layout.insertWidget(len(self.tb_checkboxes) - 1, checkbox)
|
|
47023
47781
|
|
|
47024
|
-
print(f"[
|
|
47782
|
+
print(f"[SuperLookup] ✓ Loaded {len(termbases)} termbases via db_manager")
|
|
47025
47783
|
except Exception as e:
|
|
47026
|
-
print(f"[
|
|
47784
|
+
print(f"[SuperLookup] ✗ Error loading termbases via db_manager: {e}")
|
|
47027
47785
|
import traceback
|
|
47028
47786
|
traceback.print_exc()
|
|
47029
47787
|
else:
|
|
47030
|
-
print(f"[
|
|
47788
|
+
print(f"[SuperLookup] Neither termbase_mgr nor db_manager available")
|
|
47031
47789
|
# Add placeholder label
|
|
47032
47790
|
placeholder = QLabel("No database connection - Glossaries unavailable")
|
|
47033
47791
|
placeholder.setStyleSheet("color: #999; font-style: italic;")
|
|
@@ -47838,7 +48596,7 @@ class SuperlookupTab(QWidget):
|
|
|
47838
48596
|
self.status_label.setText(f"Navigated to glossary entry: {source_term}")
|
|
47839
48597
|
|
|
47840
48598
|
except Exception as e:
|
|
47841
|
-
print(f"[
|
|
48599
|
+
print(f"[SuperLookup] Error navigating to glossary: {e}")
|
|
47842
48600
|
self.status_label.setText(f"Error navigating to glossary: {e}")
|
|
47843
48601
|
|
|
47844
48602
|
def _select_first_term_in_table(self, main):
|
|
@@ -47852,7 +48610,7 @@ class SuperlookupTab(QWidget):
|
|
|
47852
48610
|
if item:
|
|
47853
48611
|
table.scrollToItem(item)
|
|
47854
48612
|
except Exception as e:
|
|
47855
|
-
print(f"[
|
|
48613
|
+
print(f"[SuperLookup] Error selecting term: {e}")
|
|
47856
48614
|
|
|
47857
48615
|
def display_mt_results(self, results):
|
|
47858
48616
|
"""Display MT results in the table"""
|
|
@@ -48387,7 +49145,7 @@ class SuperlookupTab(QWidget):
|
|
|
48387
49145
|
if self.main_window and hasattr(self.main_window, 'general_settings'):
|
|
48388
49146
|
saved_path = self.main_window.general_settings.get('autohotkey_path', '')
|
|
48389
49147
|
if saved_path and os.path.exists(saved_path):
|
|
48390
|
-
print(f"[
|
|
49148
|
+
print(f"[SuperLookup] Using saved AutoHotkey path: {saved_path}")
|
|
48391
49149
|
return saved_path, 'saved'
|
|
48392
49150
|
|
|
48393
49151
|
# Standard installation paths
|
|
@@ -48405,7 +49163,7 @@ class SuperlookupTab(QWidget):
|
|
|
48405
49163
|
|
|
48406
49164
|
for path in ahk_paths:
|
|
48407
49165
|
if os.path.exists(path):
|
|
48408
|
-
print(f"[
|
|
49166
|
+
print(f"[SuperLookup] Detected AutoHotkey at: {path}")
|
|
48409
49167
|
return path, 'detected'
|
|
48410
49168
|
|
|
48411
49169
|
return None, None
|
|
@@ -48532,7 +49290,7 @@ class SuperlookupTab(QWidget):
|
|
|
48532
49290
|
if self.main_window and hasattr(self.main_window, 'general_settings'):
|
|
48533
49291
|
self.main_window.general_settings['autohotkey_path'] = file_path
|
|
48534
49292
|
self.main_window.save_general_settings()
|
|
48535
|
-
print(f"[
|
|
49293
|
+
print(f"[SuperLookup] Saved AutoHotkey path: {file_path}")
|
|
48536
49294
|
|
|
48537
49295
|
self._ahk_setup_status.setText(f"✓ Saved: {file_path}\n\nRestart Supervertaler to use this path.")
|
|
48538
49296
|
|
|
@@ -48553,11 +49311,11 @@ class SuperlookupTab(QWidget):
|
|
|
48553
49311
|
Note: AutoHotkey is Windows-only, so skip on Linux/Mac.
|
|
48554
49312
|
"""
|
|
48555
49313
|
global _ahk_process
|
|
48556
|
-
print("[
|
|
49314
|
+
print("[SuperLookup] register_global_hotkey called")
|
|
48557
49315
|
|
|
48558
49316
|
# AutoHotkey is Windows-only - skip on other platforms
|
|
48559
49317
|
if sys.platform != 'win32':
|
|
48560
|
-
print("[
|
|
49318
|
+
print("[SuperLookup] Skipping AutoHotkey registration (not Windows)")
|
|
48561
49319
|
self.hotkey_registered = False
|
|
48562
49320
|
return
|
|
48563
49321
|
|
|
@@ -48571,24 +49329,24 @@ class SuperlookupTab(QWidget):
|
|
|
48571
49329
|
"""
|
|
48572
49330
|
try:
|
|
48573
49331
|
from ahk import AHK
|
|
48574
|
-
print("[
|
|
49332
|
+
print("[SuperLookup] ahk library available, attempting to use it...")
|
|
48575
49333
|
|
|
48576
49334
|
# Find AutoHotkey executable using shared function
|
|
48577
49335
|
ahk_exe, source = self._find_autohotkey_executable()
|
|
48578
49336
|
|
|
48579
49337
|
# Create AHK instance (with executable path if found)
|
|
48580
49338
|
if ahk_exe:
|
|
48581
|
-
print(f"[
|
|
49339
|
+
print(f"[SuperLookup] Using AutoHotkey at: {ahk_exe} (source: {source})")
|
|
48582
49340
|
self._ahk = AHK(executable_path=ahk_exe)
|
|
48583
49341
|
else:
|
|
48584
49342
|
# Let it try to find AHK on PATH (may fail)
|
|
48585
49343
|
self._ahk = AHK()
|
|
48586
|
-
print(f"[
|
|
49344
|
+
print(f"[SuperLookup] AHK instance created: {self._ahk}")
|
|
48587
49345
|
|
|
48588
49346
|
# Define hotkey callback
|
|
48589
49347
|
def on_hotkey():
|
|
48590
49348
|
"""Called when Ctrl+Alt+L is pressed"""
|
|
48591
|
-
print("[
|
|
49349
|
+
print("[SuperLookup] Hotkey triggered via ahk library!")
|
|
48592
49350
|
try:
|
|
48593
49351
|
# Copy selection to clipboard
|
|
48594
49352
|
self._ahk.send('^c') # Ctrl+C
|
|
@@ -48601,7 +49359,7 @@ class SuperlookupTab(QWidget):
|
|
|
48601
49359
|
try:
|
|
48602
49360
|
self._ahk.win_activate('Supervertaler')
|
|
48603
49361
|
except Exception as e:
|
|
48604
|
-
print(f"[
|
|
49362
|
+
print(f"[SuperLookup] win_activate error (non-critical): {e}")
|
|
48605
49363
|
|
|
48606
49364
|
# Trigger lookup in main thread
|
|
48607
49365
|
if text:
|
|
@@ -48609,22 +49367,22 @@ class SuperlookupTab(QWidget):
|
|
|
48609
49367
|
QTimer.singleShot(0, lambda: self.on_ahk_capture(text))
|
|
48610
49368
|
|
|
48611
49369
|
except Exception as e:
|
|
48612
|
-
print(f"[
|
|
49370
|
+
print(f"[SuperLookup] Error in hotkey callback: {e}")
|
|
48613
49371
|
|
|
48614
49372
|
# Register the hotkey
|
|
48615
49373
|
self._ahk.add_hotkey('^!l', callback=on_hotkey) # Ctrl+Alt+L
|
|
48616
49374
|
self._ahk.start_hotkeys()
|
|
48617
49375
|
|
|
48618
|
-
print("[
|
|
49376
|
+
print("[SuperLookup] ✓ Hotkey registered via ahk library: Ctrl+Alt+L")
|
|
48619
49377
|
self.hotkey_registered = True
|
|
48620
49378
|
self._using_ahk_library = True
|
|
48621
49379
|
return True
|
|
48622
49380
|
|
|
48623
49381
|
except ImportError:
|
|
48624
|
-
print("[
|
|
49382
|
+
print("[SuperLookup] ahk library not installed (pip install ahk)")
|
|
48625
49383
|
return False
|
|
48626
49384
|
except Exception as e:
|
|
48627
|
-
print(f"[
|
|
49385
|
+
print(f"[SuperLookup] ahk library method failed: {e}")
|
|
48628
49386
|
# Clean up on failure
|
|
48629
49387
|
if hasattr(self, '_ahk'):
|
|
48630
49388
|
try:
|
|
@@ -48705,11 +49463,13 @@ class SuperlookupTab(QWidget):
|
|
|
48705
49463
|
self.hotkey_registered = False
|
|
48706
49464
|
|
|
48707
49465
|
def start_file_watcher(self):
|
|
48708
|
-
"""Watch for signal
|
|
49466
|
+
"""Watch for signal files from AHK (Superlookup and MT Quick Lookup)"""
|
|
48709
49467
|
self.signal_file = Path(__file__).parent / "lookup_signal.txt"
|
|
48710
49468
|
self.capture_file = Path(__file__).parent / "temp_capture.txt"
|
|
48711
|
-
|
|
48712
|
-
|
|
49469
|
+
self.mt_lookup_signal_file = Path(__file__).parent / "mt_lookup_signal.txt"
|
|
49470
|
+
|
|
49471
|
+
print(f"[SuperLookup] File watcher started, watching: {self.signal_file}")
|
|
49472
|
+
print(f"[QuickTrans] File watcher started, watching: {self.mt_lookup_signal_file}")
|
|
48713
49473
|
|
|
48714
49474
|
# Create timer to check for signal file
|
|
48715
49475
|
self.file_check_timer = QTimer()
|
|
@@ -48718,27 +49478,57 @@ class SuperlookupTab(QWidget):
|
|
|
48718
49478
|
|
|
48719
49479
|
def check_for_signal(self):
|
|
48720
49480
|
"""Check if AHK wrote a signal file"""
|
|
49481
|
+
# Check for Superlookup signal
|
|
48721
49482
|
if self.signal_file.exists():
|
|
48722
|
-
print(f"[
|
|
49483
|
+
print(f"[SuperLookup] Signal file detected!")
|
|
48723
49484
|
try:
|
|
48724
49485
|
# Delete signal file
|
|
48725
49486
|
self.signal_file.unlink()
|
|
48726
|
-
print(f"[
|
|
48727
|
-
|
|
49487
|
+
print(f"[SuperLookup] Signal file deleted")
|
|
49488
|
+
|
|
48728
49489
|
# Get text from clipboard (AHK already copied it)
|
|
48729
49490
|
time.sleep(0.1) # Give clipboard a moment
|
|
48730
49491
|
text = pyperclip.paste()
|
|
48731
|
-
|
|
49492
|
+
|
|
48732
49493
|
# Trigger lookup
|
|
48733
49494
|
if text:
|
|
48734
49495
|
self.on_ahk_capture(text)
|
|
48735
49496
|
except Exception as e:
|
|
48736
|
-
print(f"[
|
|
49497
|
+
print(f"[SuperLookup] Error reading capture: {e}")
|
|
49498
|
+
|
|
49499
|
+
# Check for MT Quick Lookup signal
|
|
49500
|
+
if hasattr(self, 'mt_lookup_signal_file') and self.mt_lookup_signal_file.exists():
|
|
49501
|
+
print(f"[QuickTrans] Signal file detected!")
|
|
49502
|
+
try:
|
|
49503
|
+
# Small delay to let AHK finish writing/close the file
|
|
49504
|
+
time.sleep(0.05)
|
|
49505
|
+
|
|
49506
|
+
# Delete signal file with retry for file lock
|
|
49507
|
+
for attempt in range(3):
|
|
49508
|
+
try:
|
|
49509
|
+
self.mt_lookup_signal_file.unlink()
|
|
49510
|
+
print(f"[QuickTrans] Signal file deleted")
|
|
49511
|
+
break
|
|
49512
|
+
except PermissionError:
|
|
49513
|
+
if attempt < 2:
|
|
49514
|
+
time.sleep(0.05)
|
|
49515
|
+
else:
|
|
49516
|
+
raise
|
|
49517
|
+
|
|
49518
|
+
# Get text from clipboard (AHK already copied it)
|
|
49519
|
+
time.sleep(0.1) # Give clipboard a moment
|
|
49520
|
+
text = pyperclip.paste()
|
|
49521
|
+
|
|
49522
|
+
# Trigger MT Quick Lookup
|
|
49523
|
+
if text:
|
|
49524
|
+
self.on_ahk_mt_lookup_capture(text)
|
|
49525
|
+
except Exception as e:
|
|
49526
|
+
print(f"[QuickTrans] Error reading capture: {e}")
|
|
48737
49527
|
|
|
48738
49528
|
def on_ahk_capture(self, text):
|
|
48739
49529
|
"""Handle text captured by AHK"""
|
|
48740
49530
|
try:
|
|
48741
|
-
print(f"[
|
|
49531
|
+
print(f"[SuperLookup] on_ahk_capture called with text: {text[:50]}...")
|
|
48742
49532
|
|
|
48743
49533
|
# Bring Supervertaler to foreground
|
|
48744
49534
|
main_window = self.window()
|
|
@@ -48768,39 +49558,96 @@ class SuperlookupTab(QWidget):
|
|
|
48768
49558
|
QTimer.singleShot(250, lambda: self.show_superlookup(text))
|
|
48769
49559
|
|
|
48770
49560
|
except Exception as e:
|
|
48771
|
-
print(f"[
|
|
48772
|
-
|
|
49561
|
+
print(f"[SuperLookup] Error handling capture: {e}")
|
|
49562
|
+
|
|
49563
|
+
def on_ahk_mt_lookup_capture(self, text):
|
|
49564
|
+
"""Handle MT Quick Lookup text captured by AHK (Ctrl+Alt+M)"""
|
|
49565
|
+
try:
|
|
49566
|
+
print(f"[QuickTrans] on_ahk_mt_lookup_capture called with text: {text[:50]}...")
|
|
49567
|
+
|
|
49568
|
+
# Show popup directly without bringing Supervertaler to foreground
|
|
49569
|
+
# The popup has WindowStaysOnTopHint so it will appear over any app
|
|
49570
|
+
QTimer.singleShot(100, lambda: self.show_mt_quick_lookup_from_ahk(text))
|
|
49571
|
+
|
|
49572
|
+
except Exception as e:
|
|
49573
|
+
print(f"[QuickTrans] Error handling capture: {e}")
|
|
49574
|
+
|
|
49575
|
+
def show_mt_quick_lookup_from_ahk(self, text):
|
|
49576
|
+
"""Show MT Quick Lookup popup with text from AHK capture"""
|
|
49577
|
+
try:
|
|
49578
|
+
print(f"[QuickTrans] show_mt_quick_lookup_from_ahk called with text: {text[:50]}...")
|
|
49579
|
+
|
|
49580
|
+
# Get main window reference for settings access
|
|
49581
|
+
main_window = self.main_window
|
|
49582
|
+
if not main_window:
|
|
49583
|
+
main_window = self.window()
|
|
49584
|
+
|
|
49585
|
+
if not main_window:
|
|
49586
|
+
print("[QuickTrans] ERROR: Could not find main window")
|
|
49587
|
+
return
|
|
49588
|
+
|
|
49589
|
+
# Get language settings
|
|
49590
|
+
source_lang = getattr(main_window, 'source_language', 'English')
|
|
49591
|
+
target_lang = getattr(main_window, 'target_language', 'Dutch')
|
|
49592
|
+
|
|
49593
|
+
# Import and show MT Quick Lookup popup
|
|
49594
|
+
from modules.quicktrans import MTQuickPopup
|
|
49595
|
+
|
|
49596
|
+
# Create popup without Qt parent so it can appear independently over any app
|
|
49597
|
+
# Pass main_window as parent_app for API access
|
|
49598
|
+
popup = MTQuickPopup(
|
|
49599
|
+
parent_app=main_window,
|
|
49600
|
+
source_text=text,
|
|
49601
|
+
source_lang=source_lang,
|
|
49602
|
+
target_lang=target_lang,
|
|
49603
|
+
parent=None # No Qt parent - allows independent window
|
|
49604
|
+
)
|
|
49605
|
+
|
|
49606
|
+
# Store reference to prevent garbage collection
|
|
49607
|
+
self._ahk_mt_popup = popup
|
|
49608
|
+
|
|
49609
|
+
# Show and ensure it's on top
|
|
49610
|
+
popup.show()
|
|
49611
|
+
popup.raise_()
|
|
49612
|
+
popup.activateWindow()
|
|
49613
|
+
print(f"[QuickTrans] Popup shown for text: {text[:30]}...")
|
|
49614
|
+
|
|
49615
|
+
except Exception as e:
|
|
49616
|
+
print(f"[QuickTrans] Error showing popup: {e}")
|
|
49617
|
+
import traceback
|
|
49618
|
+
traceback.print_exc()
|
|
49619
|
+
|
|
48773
49620
|
def show_superlookup(self, text):
|
|
48774
49621
|
"""Show Superlookup with pre-filled text"""
|
|
48775
49622
|
try:
|
|
48776
|
-
print(f"[
|
|
49623
|
+
print(f"[SuperLookup] show_superlookup called with text: {text[:50]}...")
|
|
48777
49624
|
|
|
48778
49625
|
# Get main window reference
|
|
48779
49626
|
main_window = self.main_window
|
|
48780
49627
|
if not main_window:
|
|
48781
49628
|
main_window = self.window()
|
|
48782
49629
|
|
|
48783
|
-
print(f"[
|
|
48784
|
-
print(f"[
|
|
48785
|
-
print(f"[
|
|
49630
|
+
print(f"[SuperLookup] Main window found: {main_window is not None}")
|
|
49631
|
+
print(f"[SuperLookup] Main window type: {type(main_window).__name__}")
|
|
49632
|
+
print(f"[SuperLookup] Has main_tabs: {hasattr(main_window, 'main_tabs')}")
|
|
48786
49633
|
|
|
48787
49634
|
# Switch to Tools tab (main_tabs index 3)
|
|
48788
49635
|
# Tab structure: Grid=0, Resources=1, QuickMenu=2, Tools=3, Settings=4
|
|
48789
49636
|
if hasattr(main_window, 'main_tabs'):
|
|
48790
|
-
print(f"[
|
|
49637
|
+
print(f"[SuperLookup] Current main_tab index: {main_window.main_tabs.currentIndex()}")
|
|
48791
49638
|
main_window.main_tabs.setCurrentIndex(3) # Tools tab
|
|
48792
|
-
print(f"[
|
|
49639
|
+
print(f"[SuperLookup] Switched to Tools tab (index 2)")
|
|
48793
49640
|
QApplication.processEvents() # Force GUI update
|
|
48794
49641
|
else:
|
|
48795
|
-
print(f"[
|
|
49642
|
+
print(f"[SuperLookup] WARNING: Main window has no main_tabs attribute!")
|
|
48796
49643
|
|
|
48797
49644
|
# Switch to Superlookup within modules_tabs
|
|
48798
49645
|
if hasattr(main_window, 'modules_tabs'):
|
|
48799
|
-
print(f"[
|
|
49646
|
+
print(f"[SuperLookup] Current modules_tab index: {main_window.modules_tabs.currentIndex()}")
|
|
48800
49647
|
for i in range(main_window.modules_tabs.count()):
|
|
48801
49648
|
if "Superlookup" in main_window.modules_tabs.tabText(i):
|
|
48802
49649
|
main_window.modules_tabs.setCurrentIndex(i)
|
|
48803
|
-
print(f"[
|
|
49650
|
+
print(f"[SuperLookup] Switched to Superlookup tab (index {i})")
|
|
48804
49651
|
QApplication.processEvents() # Force GUI update
|
|
48805
49652
|
break
|
|
48806
49653
|
|
|
@@ -48808,23 +49655,23 @@ class SuperlookupTab(QWidget):
|
|
|
48808
49655
|
QTimer.singleShot(100, lambda: self._fill_and_search(text))
|
|
48809
49656
|
|
|
48810
49657
|
except Exception as e:
|
|
48811
|
-
print(f"[
|
|
49658
|
+
print(f"[SuperLookup] Error showing lookup: {e}")
|
|
48812
49659
|
|
|
48813
49660
|
def _fill_and_search(self, text):
|
|
48814
49661
|
"""Fill in text and trigger search (called after tab switching completes)"""
|
|
48815
49662
|
try:
|
|
48816
|
-
print(f"[
|
|
49663
|
+
print(f"[SuperLookup] _fill_and_search called")
|
|
48817
49664
|
# Fill in text and trigger lookup
|
|
48818
49665
|
if hasattr(self, 'source_text'):
|
|
48819
49666
|
self.source_text.setCurrentText(text)
|
|
48820
|
-
print(f"[
|
|
49667
|
+
print(f"[SuperLookup] Text filled in source_text field")
|
|
48821
49668
|
# Trigger lookup by calling perform_lookup directly
|
|
48822
49669
|
self.perform_lookup()
|
|
48823
|
-
print(f"[
|
|
49670
|
+
print(f"[SuperLookup] perform_lookup() called")
|
|
48824
49671
|
else:
|
|
48825
|
-
print(f"[
|
|
49672
|
+
print(f"[SuperLookup] ERROR: source_text widget not found!")
|
|
48826
49673
|
except Exception as e:
|
|
48827
|
-
print(f"[
|
|
49674
|
+
print(f"[SuperLookup] Error in _fill_and_search: {e}")
|
|
48828
49675
|
import traceback
|
|
48829
49676
|
traceback.print_exc()
|
|
48830
49677
|
|