supervertaler 1.9.163__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- Supervertaler.py +48473 -0
- modules/__init__.py +10 -0
- modules/ai_actions.py +964 -0
- modules/ai_attachment_manager.py +343 -0
- modules/ai_file_viewer_dialog.py +210 -0
- modules/autofingers_engine.py +466 -0
- modules/cafetran_docx_handler.py +379 -0
- modules/config_manager.py +469 -0
- modules/database_manager.py +1911 -0
- modules/database_migrations.py +417 -0
- modules/dejavurtf_handler.py +779 -0
- modules/document_analyzer.py +427 -0
- modules/docx_handler.py +689 -0
- modules/encoding_repair.py +319 -0
- modules/encoding_repair_Qt.py +393 -0
- modules/encoding_repair_ui.py +481 -0
- modules/feature_manager.py +350 -0
- modules/figure_context_manager.py +340 -0
- modules/file_dialog_helper.py +148 -0
- modules/find_replace.py +164 -0
- modules/find_replace_qt.py +457 -0
- modules/glossary_manager.py +433 -0
- modules/image_extractor.py +188 -0
- modules/keyboard_shortcuts_widget.py +571 -0
- modules/llm_clients.py +1211 -0
- modules/llm_leaderboard.py +737 -0
- modules/llm_superbench_ui.py +1401 -0
- modules/local_llm_setup.py +1104 -0
- modules/model_update_dialog.py +381 -0
- modules/model_version_checker.py +373 -0
- modules/mqxliff_handler.py +638 -0
- modules/non_translatables_manager.py +743 -0
- modules/pdf_rescue_Qt.py +1822 -0
- modules/pdf_rescue_tkinter.py +909 -0
- modules/phrase_docx_handler.py +516 -0
- modules/project_home_panel.py +209 -0
- modules/prompt_assistant.py +357 -0
- modules/prompt_library.py +689 -0
- modules/prompt_library_migration.py +447 -0
- modules/quick_access_sidebar.py +282 -0
- modules/ribbon_widget.py +597 -0
- modules/sdlppx_handler.py +874 -0
- modules/setup_wizard.py +353 -0
- modules/shortcut_manager.py +932 -0
- modules/simple_segmenter.py +128 -0
- modules/spellcheck_manager.py +727 -0
- modules/statuses.py +207 -0
- modules/style_guide_manager.py +315 -0
- modules/superbench_ui.py +1319 -0
- modules/superbrowser.py +329 -0
- modules/supercleaner.py +600 -0
- modules/supercleaner_ui.py +444 -0
- modules/superdocs.py +19 -0
- modules/superdocs_viewer_qt.py +382 -0
- modules/superlookup.py +252 -0
- modules/tag_cleaner.py +260 -0
- modules/tag_manager.py +351 -0
- modules/term_extractor.py +270 -0
- modules/termbase_entry_editor.py +842 -0
- modules/termbase_import_export.py +488 -0
- modules/termbase_manager.py +1060 -0
- modules/termview_widget.py +1176 -0
- modules/theme_manager.py +499 -0
- modules/tm_editor_dialog.py +99 -0
- modules/tm_manager_qt.py +1280 -0
- modules/tm_metadata_manager.py +545 -0
- modules/tmx_editor.py +1461 -0
- modules/tmx_editor_qt.py +2784 -0
- modules/tmx_generator.py +284 -0
- modules/tracked_changes.py +900 -0
- modules/trados_docx_handler.py +430 -0
- modules/translation_memory.py +715 -0
- modules/translation_results_panel.py +2134 -0
- modules/translation_services.py +282 -0
- modules/unified_prompt_library.py +659 -0
- modules/unified_prompt_manager_qt.py +3951 -0
- modules/voice_commands.py +920 -0
- modules/voice_dictation.py +477 -0
- modules/voice_dictation_lite.py +249 -0
- supervertaler-1.9.163.dist-info/METADATA +906 -0
- supervertaler-1.9.163.dist-info/RECORD +85 -0
- supervertaler-1.9.163.dist-info/WHEEL +5 -0
- supervertaler-1.9.163.dist-info/entry_points.txt +2 -0
- supervertaler-1.9.163.dist-info/licenses/LICENSE +21 -0
- supervertaler-1.9.163.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,842 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Glossary Entry Editor Dialog
|
|
3
|
+
|
|
4
|
+
Dialog for editing individual glossary entries with all metadata fields.
|
|
5
|
+
Can be opened from translation results panel (edit button or right-click menu).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from PyQt6.QtWidgets import (
|
|
9
|
+
QDialog, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit,
|
|
10
|
+
QTextEdit, QSpinBox, QCheckBox, QPushButton, QGroupBox,
|
|
11
|
+
QMessageBox, QListWidget, QListWidgetItem, QMenu, QScrollArea,
|
|
12
|
+
QWidget, QToolButton, QApplication
|
|
13
|
+
)
|
|
14
|
+
from PyQt6.QtCore import Qt
|
|
15
|
+
from PyQt6.QtGui import QColor
|
|
16
|
+
from typing import Optional
|
|
17
|
+
|
|
18
|
+
class CheckmarkCheckBox(QCheckBox):
|
|
19
|
+
"""Custom checkbox with green background and white checkmark when checked"""
|
|
20
|
+
|
|
21
|
+
def __init__(self, text="", parent=None):
|
|
22
|
+
super().__init__(text, parent)
|
|
23
|
+
self.setCheckable(True)
|
|
24
|
+
self.setEnabled(True)
|
|
25
|
+
self.setStyleSheet("""
|
|
26
|
+
QCheckBox {
|
|
27
|
+
font-size: 9pt;
|
|
28
|
+
spacing: 6px;
|
|
29
|
+
}
|
|
30
|
+
QCheckBox::indicator {
|
|
31
|
+
width: 16px;
|
|
32
|
+
height: 16px;
|
|
33
|
+
border: 2px solid #999;
|
|
34
|
+
border-radius: 3px;
|
|
35
|
+
background-color: white;
|
|
36
|
+
}
|
|
37
|
+
QCheckBox::indicator:checked {
|
|
38
|
+
background-color: #4CAF50;
|
|
39
|
+
border-color: #4CAF50;
|
|
40
|
+
}
|
|
41
|
+
QCheckBox::indicator:hover {
|
|
42
|
+
border-color: #666;
|
|
43
|
+
}
|
|
44
|
+
QCheckBox::indicator:checked:hover {
|
|
45
|
+
background-color: #45a049;
|
|
46
|
+
border-color: #45a049;
|
|
47
|
+
}
|
|
48
|
+
""")
|
|
49
|
+
|
|
50
|
+
def paintEvent(self, event):
|
|
51
|
+
"""Override paint event to draw white checkmark when checked"""
|
|
52
|
+
super().paintEvent(event)
|
|
53
|
+
|
|
54
|
+
if self.isChecked():
|
|
55
|
+
from PyQt6.QtWidgets import QStyleOptionButton
|
|
56
|
+
from PyQt6.QtGui import QPainter, QPen, QColor
|
|
57
|
+
from PyQt6.QtCore import QPointF, Qt
|
|
58
|
+
|
|
59
|
+
opt = QStyleOptionButton()
|
|
60
|
+
self.initStyleOption(opt)
|
|
61
|
+
indicator_rect = self.style().subElementRect(
|
|
62
|
+
self.style().SubElement.SE_CheckBoxIndicator,
|
|
63
|
+
opt,
|
|
64
|
+
self
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
if indicator_rect.isValid():
|
|
68
|
+
painter = QPainter(self)
|
|
69
|
+
painter.setRenderHint(QPainter.RenderHint.Antialiasing)
|
|
70
|
+
pen_width = max(2.0, min(indicator_rect.width(), indicator_rect.height()) * 0.12)
|
|
71
|
+
painter.setPen(QPen(QColor(255, 255, 255), pen_width, Qt.PenStyle.SolidLine, Qt.PenCapStyle.RoundCap, Qt.PenJoinStyle.RoundJoin))
|
|
72
|
+
painter.setBrush(QColor(255, 255, 255))
|
|
73
|
+
|
|
74
|
+
x = indicator_rect.x()
|
|
75
|
+
y = indicator_rect.y()
|
|
76
|
+
w = indicator_rect.width()
|
|
77
|
+
h = indicator_rect.height()
|
|
78
|
+
|
|
79
|
+
padding = min(w, h) * 0.15
|
|
80
|
+
x += padding
|
|
81
|
+
y += padding
|
|
82
|
+
w -= padding * 2
|
|
83
|
+
h -= padding * 2
|
|
84
|
+
|
|
85
|
+
check_x1 = x + w * 0.10
|
|
86
|
+
check_y1 = y + h * 0.50
|
|
87
|
+
check_x2 = x + w * 0.35
|
|
88
|
+
check_y2 = y + h * 0.70
|
|
89
|
+
check_x3 = x + w * 0.90
|
|
90
|
+
check_y3 = y + h * 0.25
|
|
91
|
+
|
|
92
|
+
painter.drawLine(QPointF(check_x2, check_y2), QPointF(check_x3, check_y3))
|
|
93
|
+
painter.drawLine(QPointF(check_x1, check_y1), QPointF(check_x2, check_y2))
|
|
94
|
+
|
|
95
|
+
painter.end()
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
class TermbaseEntryEditor(QDialog):
|
|
100
|
+
"""Dialog for editing a termbase entry"""
|
|
101
|
+
|
|
102
|
+
def __init__(self, parent=None, db_manager=None, termbase_id: Optional[int] = None, term_id: Optional[int] = None):
|
|
103
|
+
"""
|
|
104
|
+
Initialize termbase entry editor
|
|
105
|
+
|
|
106
|
+
Args:
|
|
107
|
+
parent: Parent widget
|
|
108
|
+
db_manager: DatabaseManager instance
|
|
109
|
+
termbase_id: Termbase ID
|
|
110
|
+
term_id: Term ID to edit (if None, creates new term)
|
|
111
|
+
"""
|
|
112
|
+
super().__init__(parent)
|
|
113
|
+
self.db_manager = db_manager
|
|
114
|
+
self.termbase_id = termbase_id
|
|
115
|
+
self.term_id = term_id
|
|
116
|
+
self.term_data = None
|
|
117
|
+
|
|
118
|
+
self.setWindowTitle("Edit Glossary Entry" if term_id else "New Glossary Entry")
|
|
119
|
+
self.setModal(True)
|
|
120
|
+
self.setMinimumWidth(550)
|
|
121
|
+
|
|
122
|
+
# Auto-resize to fit screen (max 85% of screen height)
|
|
123
|
+
screen = QApplication.primaryScreen().availableGeometry()
|
|
124
|
+
max_height = int(screen.height() * 0.85)
|
|
125
|
+
self.setMaximumHeight(max_height)
|
|
126
|
+
|
|
127
|
+
# Start with very compact size for laptops
|
|
128
|
+
self.resize(600, min(550, max_height))
|
|
129
|
+
|
|
130
|
+
self.setup_ui()
|
|
131
|
+
|
|
132
|
+
# Load existing term data if editing
|
|
133
|
+
if term_id and db_manager:
|
|
134
|
+
self.load_term_data()
|
|
135
|
+
|
|
136
|
+
def setup_ui(self):
|
|
137
|
+
"""Setup the user interface"""
|
|
138
|
+
main_layout = QVBoxLayout(self)
|
|
139
|
+
main_layout.setContentsMargins(0, 0, 0, 0)
|
|
140
|
+
|
|
141
|
+
# Create scroll area for all content
|
|
142
|
+
scroll = QScrollArea()
|
|
143
|
+
scroll.setWidgetResizable(True)
|
|
144
|
+
scroll.setFrameShape(QScrollArea.Shape.NoFrame)
|
|
145
|
+
|
|
146
|
+
content_widget = QWidget()
|
|
147
|
+
layout = QVBoxLayout(content_widget)
|
|
148
|
+
layout.setSpacing(4)
|
|
149
|
+
layout.setContentsMargins(6, 6, 6, 6)
|
|
150
|
+
|
|
151
|
+
# Header
|
|
152
|
+
header = QLabel("📖 Glossary Entry Editor")
|
|
153
|
+
header.setStyleSheet("font-size: 12px; font-weight: bold; color: #333; padding: 4px;")
|
|
154
|
+
layout.addWidget(header)
|
|
155
|
+
|
|
156
|
+
# Terms group
|
|
157
|
+
terms_group = QGroupBox("Terms")
|
|
158
|
+
terms_layout = QVBoxLayout()
|
|
159
|
+
terms_layout.setSpacing(4)
|
|
160
|
+
|
|
161
|
+
# Source term
|
|
162
|
+
source_label = QLabel("Source Term:")
|
|
163
|
+
source_label.setStyleSheet("font-weight: bold;")
|
|
164
|
+
terms_layout.addWidget(source_label)
|
|
165
|
+
|
|
166
|
+
self.source_edit = QLineEdit()
|
|
167
|
+
self.source_edit.setPlaceholderText("Enter source language term...")
|
|
168
|
+
self.source_edit.setStyleSheet("padding: 6px; font-size: 11px;")
|
|
169
|
+
terms_layout.addWidget(self.source_edit)
|
|
170
|
+
|
|
171
|
+
# Target term
|
|
172
|
+
target_label = QLabel("Target Term:")
|
|
173
|
+
target_label.setStyleSheet("font-weight: bold;")
|
|
174
|
+
terms_layout.addWidget(target_label)
|
|
175
|
+
|
|
176
|
+
self.target_edit = QLineEdit()
|
|
177
|
+
self.target_edit.setPlaceholderText("Enter target language term...")
|
|
178
|
+
self.target_edit.setStyleSheet("padding: 6px; font-size: 11px;")
|
|
179
|
+
terms_layout.addWidget(self.target_edit)
|
|
180
|
+
|
|
181
|
+
terms_group.setLayout(terms_layout)
|
|
182
|
+
layout.addWidget(terms_group)
|
|
183
|
+
|
|
184
|
+
# Source Synonyms section (collapsible)
|
|
185
|
+
source_syn_group = QGroupBox()
|
|
186
|
+
source_syn_main_layout = QVBoxLayout()
|
|
187
|
+
|
|
188
|
+
# Header with collapse button
|
|
189
|
+
source_syn_header = QHBoxLayout()
|
|
190
|
+
self.source_syn_toggle = QToolButton()
|
|
191
|
+
self.source_syn_toggle.setText("▼")
|
|
192
|
+
self.source_syn_toggle.setStyleSheet("QToolButton { border: none; font-weight: bold; }")
|
|
193
|
+
self.source_syn_toggle.setFixedSize(20, 20)
|
|
194
|
+
self.source_syn_toggle.setCheckable(True)
|
|
195
|
+
self.source_syn_toggle.setChecked(False)
|
|
196
|
+
source_syn_header.addWidget(self.source_syn_toggle)
|
|
197
|
+
|
|
198
|
+
source_syn_label = QLabel("Source Synonyms (Optional)")
|
|
199
|
+
source_syn_label.setStyleSheet("font-weight: bold;")
|
|
200
|
+
source_syn_header.addWidget(source_syn_label)
|
|
201
|
+
source_syn_header.addStretch()
|
|
202
|
+
source_syn_main_layout.addLayout(source_syn_header)
|
|
203
|
+
|
|
204
|
+
# Collapsible content
|
|
205
|
+
self.source_syn_content = QWidget()
|
|
206
|
+
source_syn_layout = QVBoxLayout(self.source_syn_content)
|
|
207
|
+
source_syn_layout.setContentsMargins(0, 0, 0, 0)
|
|
208
|
+
self.source_syn_content.setVisible(False)
|
|
209
|
+
|
|
210
|
+
source_syn_info = QLabel("Alternative source terms. First item = preferred:")
|
|
211
|
+
source_syn_info.setStyleSheet("color: #666; font-size: 10px;")
|
|
212
|
+
source_syn_layout.addWidget(source_syn_info)
|
|
213
|
+
|
|
214
|
+
source_add_layout = QHBoxLayout()
|
|
215
|
+
self.source_synonym_edit = QLineEdit()
|
|
216
|
+
self.source_synonym_edit.setPlaceholderText("Enter source synonym...")
|
|
217
|
+
self.source_synonym_edit.setStyleSheet("padding: 4px; font-size: 10px;")
|
|
218
|
+
source_add_layout.addWidget(self.source_synonym_edit)
|
|
219
|
+
|
|
220
|
+
self.source_synonym_forbidden_check = CheckmarkCheckBox("Forbidden")
|
|
221
|
+
self.source_synonym_forbidden_check.setStyleSheet("font-size: 10px;")
|
|
222
|
+
source_add_layout.addWidget(self.source_synonym_forbidden_check)
|
|
223
|
+
|
|
224
|
+
source_add_btn = QPushButton("Add")
|
|
225
|
+
source_add_btn.setMaximumWidth(50)
|
|
226
|
+
source_add_btn.setStyleSheet("padding: 4px; font-size: 10px;")
|
|
227
|
+
source_add_btn.clicked.connect(self.add_source_synonym)
|
|
228
|
+
source_add_layout.addWidget(source_add_btn)
|
|
229
|
+
source_syn_layout.addLayout(source_add_layout)
|
|
230
|
+
|
|
231
|
+
self.source_synonym_edit.returnPressed.connect(self.add_source_synonym)
|
|
232
|
+
|
|
233
|
+
source_list_layout = QHBoxLayout()
|
|
234
|
+
self.source_synonym_list = QListWidget()
|
|
235
|
+
self.source_synonym_list.setMaximumHeight(80)
|
|
236
|
+
self.source_synonym_list.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
|
|
237
|
+
self.source_synonym_list.customContextMenuRequested.connect(self.show_source_synonym_context_menu)
|
|
238
|
+
source_list_layout.addWidget(self.source_synonym_list)
|
|
239
|
+
|
|
240
|
+
source_btn_col = QVBoxLayout()
|
|
241
|
+
source_up_btn = QPushButton("▲")
|
|
242
|
+
source_up_btn.setMaximumWidth(25)
|
|
243
|
+
source_up_btn.setToolTip("Move up")
|
|
244
|
+
source_up_btn.clicked.connect(lambda: self.move_synonym(self.source_synonym_list, -1))
|
|
245
|
+
source_btn_col.addWidget(source_up_btn)
|
|
246
|
+
|
|
247
|
+
source_down_btn = QPushButton("▼")
|
|
248
|
+
source_down_btn.setMaximumWidth(25)
|
|
249
|
+
source_down_btn.setToolTip("Move down")
|
|
250
|
+
source_down_btn.clicked.connect(lambda: self.move_synonym(self.source_synonym_list, 1))
|
|
251
|
+
source_btn_col.addWidget(source_down_btn)
|
|
252
|
+
source_btn_col.addStretch()
|
|
253
|
+
|
|
254
|
+
source_del_btn = QPushButton("✗")
|
|
255
|
+
source_del_btn.setMaximumWidth(25)
|
|
256
|
+
source_del_btn.setToolTip("Delete")
|
|
257
|
+
source_del_btn.clicked.connect(lambda: self.delete_synonym(self.source_synonym_list))
|
|
258
|
+
source_btn_col.addWidget(source_del_btn)
|
|
259
|
+
|
|
260
|
+
source_list_layout.addLayout(source_btn_col)
|
|
261
|
+
source_syn_layout.addLayout(source_list_layout)
|
|
262
|
+
|
|
263
|
+
# Add collapsible content to main layout
|
|
264
|
+
source_syn_main_layout.addWidget(self.source_syn_content)
|
|
265
|
+
source_syn_group.setLayout(source_syn_main_layout)
|
|
266
|
+
|
|
267
|
+
# Connect toggle button
|
|
268
|
+
self.source_syn_toggle.clicked.connect(lambda: self.toggle_section(self.source_syn_toggle, self.source_syn_content))
|
|
269
|
+
|
|
270
|
+
layout.addWidget(source_syn_group)
|
|
271
|
+
|
|
272
|
+
# Target Synonyms section (collapsible)
|
|
273
|
+
target_syn_group = QGroupBox()
|
|
274
|
+
target_syn_main_layout = QVBoxLayout()
|
|
275
|
+
|
|
276
|
+
# Header with collapse button
|
|
277
|
+
target_syn_header = QHBoxLayout()
|
|
278
|
+
self.target_syn_toggle = QToolButton()
|
|
279
|
+
self.target_syn_toggle.setText("▼")
|
|
280
|
+
self.target_syn_toggle.setStyleSheet("QToolButton { border: none; font-weight: bold; }")
|
|
281
|
+
self.target_syn_toggle.setFixedSize(20, 20)
|
|
282
|
+
self.target_syn_toggle.setCheckable(True)
|
|
283
|
+
self.target_syn_toggle.setChecked(False)
|
|
284
|
+
target_syn_header.addWidget(self.target_syn_toggle)
|
|
285
|
+
|
|
286
|
+
target_syn_label = QLabel("Target Synonyms (Optional)")
|
|
287
|
+
target_syn_label.setStyleSheet("font-weight: bold;")
|
|
288
|
+
target_syn_header.addWidget(target_syn_label)
|
|
289
|
+
target_syn_header.addStretch()
|
|
290
|
+
target_syn_main_layout.addLayout(target_syn_header)
|
|
291
|
+
|
|
292
|
+
# Collapsible content
|
|
293
|
+
self.target_syn_content = QWidget()
|
|
294
|
+
target_syn_layout = QVBoxLayout(self.target_syn_content)
|
|
295
|
+
target_syn_layout.setContentsMargins(0, 0, 0, 0)
|
|
296
|
+
self.target_syn_content.setVisible(False)
|
|
297
|
+
|
|
298
|
+
target_syn_info = QLabel("Alternative target terms. First item = preferred:")
|
|
299
|
+
target_syn_info.setStyleSheet("color: #666; font-size: 10px;")
|
|
300
|
+
target_syn_layout.addWidget(target_syn_info)
|
|
301
|
+
|
|
302
|
+
target_add_layout = QHBoxLayout()
|
|
303
|
+
self.target_synonym_edit = QLineEdit()
|
|
304
|
+
self.target_synonym_edit.setPlaceholderText("Enter target synonym...")
|
|
305
|
+
self.target_synonym_edit.setStyleSheet("padding: 4px; font-size: 10px;")
|
|
306
|
+
target_add_layout.addWidget(self.target_synonym_edit)
|
|
307
|
+
|
|
308
|
+
self.target_synonym_forbidden_check = CheckmarkCheckBox("Forbidden")
|
|
309
|
+
self.target_synonym_forbidden_check.setStyleSheet("font-size: 10px;")
|
|
310
|
+
target_add_layout.addWidget(self.target_synonym_forbidden_check)
|
|
311
|
+
|
|
312
|
+
target_add_btn = QPushButton("Add")
|
|
313
|
+
target_add_btn.setMaximumWidth(50)
|
|
314
|
+
target_add_btn.setStyleSheet("padding: 4px; font-size: 10px;")
|
|
315
|
+
target_add_btn.clicked.connect(self.add_target_synonym)
|
|
316
|
+
target_add_layout.addWidget(target_add_btn)
|
|
317
|
+
target_syn_layout.addLayout(target_add_layout)
|
|
318
|
+
|
|
319
|
+
self.target_synonym_edit.returnPressed.connect(self.add_target_synonym)
|
|
320
|
+
|
|
321
|
+
target_list_layout = QHBoxLayout()
|
|
322
|
+
self.target_synonym_list = QListWidget()
|
|
323
|
+
self.target_synonym_list.setMaximumHeight(80)
|
|
324
|
+
self.target_synonym_list.setContextMenuPolicy(Qt.ContextMenuPolicy.CustomContextMenu)
|
|
325
|
+
self.target_synonym_list.customContextMenuRequested.connect(self.show_target_synonym_context_menu)
|
|
326
|
+
target_list_layout.addWidget(self.target_synonym_list)
|
|
327
|
+
|
|
328
|
+
target_btn_col = QVBoxLayout()
|
|
329
|
+
target_up_btn = QPushButton("▲")
|
|
330
|
+
target_up_btn.setMaximumWidth(25)
|
|
331
|
+
target_up_btn.setToolTip("Move up")
|
|
332
|
+
target_up_btn.clicked.connect(lambda: self.move_synonym(self.target_synonym_list, -1))
|
|
333
|
+
target_btn_col.addWidget(target_up_btn)
|
|
334
|
+
|
|
335
|
+
target_down_btn = QPushButton("▼")
|
|
336
|
+
target_down_btn.setMaximumWidth(25)
|
|
337
|
+
target_down_btn.setToolTip("Move down")
|
|
338
|
+
target_down_btn.clicked.connect(lambda: self.move_synonym(self.target_synonym_list, 1))
|
|
339
|
+
target_btn_col.addWidget(target_down_btn)
|
|
340
|
+
target_btn_col.addStretch()
|
|
341
|
+
|
|
342
|
+
target_del_btn = QPushButton("✗")
|
|
343
|
+
target_del_btn.setMaximumWidth(25)
|
|
344
|
+
target_del_btn.setToolTip("Delete")
|
|
345
|
+
target_del_btn.clicked.connect(lambda: self.delete_synonym(self.target_synonym_list))
|
|
346
|
+
target_btn_col.addWidget(target_del_btn)
|
|
347
|
+
|
|
348
|
+
target_list_layout.addLayout(target_btn_col)
|
|
349
|
+
target_syn_layout.addLayout(target_list_layout)
|
|
350
|
+
|
|
351
|
+
# Add collapsible content to main layout
|
|
352
|
+
target_syn_main_layout.addWidget(self.target_syn_content)
|
|
353
|
+
target_syn_group.setLayout(target_syn_main_layout)
|
|
354
|
+
|
|
355
|
+
# Connect toggle button
|
|
356
|
+
self.target_syn_toggle.clicked.connect(lambda: self.toggle_section(self.target_syn_toggle, self.target_syn_content))
|
|
357
|
+
|
|
358
|
+
layout.addWidget(target_syn_group)
|
|
359
|
+
|
|
360
|
+
# Metadata group
|
|
361
|
+
metadata_group = QGroupBox("Metadata")
|
|
362
|
+
metadata_layout = QVBoxLayout()
|
|
363
|
+
metadata_layout.setSpacing(4)
|
|
364
|
+
|
|
365
|
+
# Priority
|
|
366
|
+
priority_layout = QHBoxLayout()
|
|
367
|
+
priority_label = QLabel("Priority (1=highest, 99=lowest):")
|
|
368
|
+
priority_label.setStyleSheet("font-weight: bold;")
|
|
369
|
+
priority_layout.addWidget(priority_label)
|
|
370
|
+
|
|
371
|
+
self.priority_spin = QSpinBox()
|
|
372
|
+
self.priority_spin.setMinimum(1)
|
|
373
|
+
self.priority_spin.setMaximum(99)
|
|
374
|
+
self.priority_spin.setValue(50)
|
|
375
|
+
self.priority_spin.setToolTip("Lower numbers = higher priority")
|
|
376
|
+
self.priority_spin.setStyleSheet("padding: 4px; font-size: 11px;")
|
|
377
|
+
priority_layout.addWidget(self.priority_spin)
|
|
378
|
+
priority_layout.addStretch()
|
|
379
|
+
|
|
380
|
+
metadata_layout.addLayout(priority_layout)
|
|
381
|
+
|
|
382
|
+
# Domain
|
|
383
|
+
domain_label = QLabel("Domain:")
|
|
384
|
+
domain_label.setStyleSheet("font-weight: bold;")
|
|
385
|
+
metadata_layout.addWidget(domain_label)
|
|
386
|
+
|
|
387
|
+
self.domain_edit = QLineEdit()
|
|
388
|
+
self.domain_edit.setPlaceholderText("e.g., Patents, Legal, Medical, IT...")
|
|
389
|
+
self.domain_edit.setStyleSheet("padding: 6px; font-size: 11px;")
|
|
390
|
+
metadata_layout.addWidget(self.domain_edit)
|
|
391
|
+
|
|
392
|
+
# Note
|
|
393
|
+
note_label = QLabel("Note:")
|
|
394
|
+
note_label.setStyleSheet("font-weight: bold;")
|
|
395
|
+
metadata_layout.addWidget(note_label)
|
|
396
|
+
|
|
397
|
+
self.note_edit = QTextEdit()
|
|
398
|
+
self.note_edit.setPlaceholderText("Usage notes, context, definition, URLs...")
|
|
399
|
+
self.note_edit.setMaximumHeight(45)
|
|
400
|
+
self.note_edit.setStyleSheet("padding: 3px; font-size: 10px;")
|
|
401
|
+
metadata_layout.addWidget(self.note_edit)
|
|
402
|
+
|
|
403
|
+
# Project
|
|
404
|
+
project_label = QLabel("Project:")
|
|
405
|
+
project_label.setStyleSheet("font-weight: bold;")
|
|
406
|
+
metadata_layout.addWidget(project_label)
|
|
407
|
+
|
|
408
|
+
self.project_edit = QLineEdit()
|
|
409
|
+
self.project_edit.setPlaceholderText("Optional project name...")
|
|
410
|
+
self.project_edit.setStyleSheet("padding: 6px; font-size: 11px;")
|
|
411
|
+
metadata_layout.addWidget(self.project_edit)
|
|
412
|
+
|
|
413
|
+
# Client
|
|
414
|
+
client_label = QLabel("Client:")
|
|
415
|
+
client_label.setStyleSheet("font-weight: bold;")
|
|
416
|
+
metadata_layout.addWidget(client_label)
|
|
417
|
+
|
|
418
|
+
self.client_edit = QLineEdit()
|
|
419
|
+
self.client_edit.setPlaceholderText("Optional client name...")
|
|
420
|
+
self.client_edit.setStyleSheet("padding: 6px; font-size: 11px;")
|
|
421
|
+
metadata_layout.addWidget(self.client_edit)
|
|
422
|
+
|
|
423
|
+
# Forbidden checkbox
|
|
424
|
+
self.forbidden_check = CheckmarkCheckBox("⚠️ Mark as FORBIDDEN term (do not use)")
|
|
425
|
+
self.forbidden_check.setStyleSheet("font-weight: bold; color: #d32f2f;")
|
|
426
|
+
metadata_layout.addWidget(self.forbidden_check)
|
|
427
|
+
|
|
428
|
+
metadata_group.setLayout(metadata_layout)
|
|
429
|
+
layout.addWidget(metadata_group)
|
|
430
|
+
|
|
431
|
+
# Buttons
|
|
432
|
+
buttons_layout = QHBoxLayout()
|
|
433
|
+
|
|
434
|
+
# Delete button (only show when editing existing term)
|
|
435
|
+
if self.term_id:
|
|
436
|
+
self.delete_btn = QPushButton("🗑️ Delete")
|
|
437
|
+
self.delete_btn.setStyleSheet("""
|
|
438
|
+
QPushButton {
|
|
439
|
+
padding: 8px 20px;
|
|
440
|
+
font-size: 11px;
|
|
441
|
+
font-weight: bold;
|
|
442
|
+
background-color: #f44336;
|
|
443
|
+
color: white;
|
|
444
|
+
border: none;
|
|
445
|
+
border-radius: 3px;
|
|
446
|
+
}
|
|
447
|
+
QPushButton:hover {
|
|
448
|
+
background-color: #d32f2f;
|
|
449
|
+
}
|
|
450
|
+
""")
|
|
451
|
+
self.delete_btn.clicked.connect(self.delete_term)
|
|
452
|
+
buttons_layout.addWidget(self.delete_btn)
|
|
453
|
+
|
|
454
|
+
buttons_layout.addStretch()
|
|
455
|
+
|
|
456
|
+
self.cancel_btn = QPushButton("Cancel")
|
|
457
|
+
self.cancel_btn.setStyleSheet("""
|
|
458
|
+
QPushButton {
|
|
459
|
+
padding: 8px 20px;
|
|
460
|
+
font-size: 11px;
|
|
461
|
+
background-color: #f5f5f5;
|
|
462
|
+
border: 1px solid #ccc;
|
|
463
|
+
border-radius: 3px;
|
|
464
|
+
}
|
|
465
|
+
QPushButton:hover {
|
|
466
|
+
background-color: #e0e0e0;
|
|
467
|
+
}
|
|
468
|
+
""")
|
|
469
|
+
self.cancel_btn.clicked.connect(self.reject)
|
|
470
|
+
buttons_layout.addWidget(self.cancel_btn)
|
|
471
|
+
|
|
472
|
+
self.save_btn = QPushButton("💾 Save")
|
|
473
|
+
self.save_btn.setStyleSheet("""
|
|
474
|
+
QPushButton {
|
|
475
|
+
padding: 8px 20px;
|
|
476
|
+
font-size: 11px;
|
|
477
|
+
font-weight: bold;
|
|
478
|
+
background-color: #4CAF50;
|
|
479
|
+
color: white;
|
|
480
|
+
border: none;
|
|
481
|
+
border-radius: 3px;
|
|
482
|
+
}
|
|
483
|
+
QPushButton:hover {
|
|
484
|
+
background-color: #45a049;
|
|
485
|
+
}
|
|
486
|
+
""")
|
|
487
|
+
self.save_btn.clicked.connect(self.save_term)
|
|
488
|
+
buttons_layout.addWidget(self.save_btn)
|
|
489
|
+
|
|
490
|
+
layout.addLayout(buttons_layout)
|
|
491
|
+
|
|
492
|
+
# Set the scroll area content
|
|
493
|
+
scroll.setWidget(content_widget)
|
|
494
|
+
main_layout.addWidget(scroll)
|
|
495
|
+
|
|
496
|
+
def toggle_section(self, toggle_btn, content_widget):
|
|
497
|
+
"""Toggle visibility of a collapsible section"""
|
|
498
|
+
is_visible = content_widget.isVisible()
|
|
499
|
+
content_widget.setVisible(not is_visible)
|
|
500
|
+
toggle_btn.setText("▼" if is_visible else "▲")
|
|
501
|
+
|
|
502
|
+
def add_source_synonym(self):
|
|
503
|
+
"""Add source synonym to list"""
|
|
504
|
+
text = self.source_synonym_edit.text().strip()
|
|
505
|
+
if text:
|
|
506
|
+
for i in range(self.source_synonym_list.count()):
|
|
507
|
+
if self.source_synonym_list.item(i).data(Qt.ItemDataRole.UserRole)['text'] == text:
|
|
508
|
+
QMessageBox.warning(self, "Duplicate", "Synonym already added")
|
|
509
|
+
return
|
|
510
|
+
|
|
511
|
+
forbidden = self.source_synonym_forbidden_check.isChecked()
|
|
512
|
+
display = f"{'🚫 ' if forbidden else ''}{text}"
|
|
513
|
+
item = QListWidgetItem(display)
|
|
514
|
+
item.setData(Qt.ItemDataRole.UserRole, {'text': text, 'forbidden': forbidden})
|
|
515
|
+
if forbidden:
|
|
516
|
+
item.setForeground(QColor('#d32f2f'))
|
|
517
|
+
self.source_synonym_list.addItem(item)
|
|
518
|
+
self.source_synonym_edit.clear()
|
|
519
|
+
self.source_synonym_forbidden_check.setChecked(False)
|
|
520
|
+
|
|
521
|
+
def add_target_synonym(self):
|
|
522
|
+
"""Add target synonym to list"""
|
|
523
|
+
text = self.target_synonym_edit.text().strip()
|
|
524
|
+
if text:
|
|
525
|
+
for i in range(self.target_synonym_list.count()):
|
|
526
|
+
if self.target_synonym_list.item(i).data(Qt.ItemDataRole.UserRole)['text'] == text:
|
|
527
|
+
QMessageBox.warning(self, "Duplicate", "Synonym already added")
|
|
528
|
+
return
|
|
529
|
+
|
|
530
|
+
forbidden = self.target_synonym_forbidden_check.isChecked()
|
|
531
|
+
display = f"{'🚫 ' if forbidden else ''}{text}"
|
|
532
|
+
item = QListWidgetItem(display)
|
|
533
|
+
item.setData(Qt.ItemDataRole.UserRole, {'text': text, 'forbidden': forbidden})
|
|
534
|
+
if forbidden:
|
|
535
|
+
item.setForeground(QColor('#d32f2f'))
|
|
536
|
+
self.target_synonym_list.addItem(item)
|
|
537
|
+
self.target_synonym_edit.clear()
|
|
538
|
+
self.target_synonym_forbidden_check.setChecked(False)
|
|
539
|
+
|
|
540
|
+
def move_synonym(self, list_widget, direction):
|
|
541
|
+
"""Move synonym up (-1) or down (1)"""
|
|
542
|
+
row = list_widget.currentRow()
|
|
543
|
+
if row < 0:
|
|
544
|
+
return
|
|
545
|
+
new_row = row + direction
|
|
546
|
+
if 0 <= new_row < list_widget.count():
|
|
547
|
+
item = list_widget.takeItem(row)
|
|
548
|
+
list_widget.insertItem(new_row, item)
|
|
549
|
+
list_widget.setCurrentRow(new_row)
|
|
550
|
+
|
|
551
|
+
def delete_synonym(self, list_widget):
|
|
552
|
+
"""Delete selected synonym"""
|
|
553
|
+
row = list_widget.currentRow()
|
|
554
|
+
if row >= 0:
|
|
555
|
+
list_widget.takeItem(row)
|
|
556
|
+
|
|
557
|
+
def show_source_synonym_context_menu(self, position):
|
|
558
|
+
"""Show context menu for source synonyms"""
|
|
559
|
+
self._show_synonym_context_menu(self.source_synonym_list, position)
|
|
560
|
+
|
|
561
|
+
def show_target_synonym_context_menu(self, position):
|
|
562
|
+
"""Show context menu for target synonyms"""
|
|
563
|
+
self._show_synonym_context_menu(self.target_synonym_list, position)
|
|
564
|
+
|
|
565
|
+
def _show_synonym_context_menu(self, list_widget, position):
|
|
566
|
+
"""Show context menu for synonym list"""
|
|
567
|
+
if list_widget.count() == 0:
|
|
568
|
+
return
|
|
569
|
+
|
|
570
|
+
item = list_widget.currentItem()
|
|
571
|
+
if not item:
|
|
572
|
+
return
|
|
573
|
+
|
|
574
|
+
menu = QMenu()
|
|
575
|
+
data = item.data(Qt.ItemDataRole.UserRole)
|
|
576
|
+
is_forbidden = data.get('forbidden', False)
|
|
577
|
+
|
|
578
|
+
toggle_action = menu.addAction("Mark as Allowed" if is_forbidden else "Mark as Forbidden")
|
|
579
|
+
menu.addSeparator()
|
|
580
|
+
delete_action = menu.addAction("Delete")
|
|
581
|
+
|
|
582
|
+
action = menu.exec(list_widget.mapToGlobal(position))
|
|
583
|
+
|
|
584
|
+
if action == toggle_action:
|
|
585
|
+
data['forbidden'] = not is_forbidden
|
|
586
|
+
text = data['text']
|
|
587
|
+
display = f"{'🚫 ' if data['forbidden'] else ''}{text}"
|
|
588
|
+
item.setText(display)
|
|
589
|
+
item.setData(Qt.ItemDataRole.UserRole, data)
|
|
590
|
+
item.setForeground(QColor('#d32f2f') if data['forbidden'] else QColor('#000000'))
|
|
591
|
+
elif action == delete_action:
|
|
592
|
+
list_widget.takeItem(list_widget.row(item))
|
|
593
|
+
|
|
594
|
+
def load_term_data(self):
|
|
595
|
+
"""Load existing term data from database"""
|
|
596
|
+
if not self.db_manager or not self.term_id:
|
|
597
|
+
return
|
|
598
|
+
|
|
599
|
+
try:
|
|
600
|
+
cursor = self.db_manager.cursor
|
|
601
|
+
cursor.execute("""
|
|
602
|
+
SELECT source_term, target_term, priority, domain, definition, forbidden,
|
|
603
|
+
notes, project, client
|
|
604
|
+
FROM termbase_terms
|
|
605
|
+
WHERE id = ?
|
|
606
|
+
""", (self.term_id,))
|
|
607
|
+
|
|
608
|
+
row = cursor.fetchone()
|
|
609
|
+
if row:
|
|
610
|
+
self.term_data = {
|
|
611
|
+
'source_term': row[0],
|
|
612
|
+
'target_term': row[1],
|
|
613
|
+
'priority': row[2] or 50,
|
|
614
|
+
'domain': row[3] or '',
|
|
615
|
+
'definition': row[4] or '', # Legacy field
|
|
616
|
+
'forbidden': row[5] or False,
|
|
617
|
+
'note': row[6] or '',
|
|
618
|
+
'project': row[7] or '',
|
|
619
|
+
'client': row[8] or ''
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
# Populate fields
|
|
623
|
+
self.source_edit.setText(self.term_data['source_term'])
|
|
624
|
+
self.target_edit.setText(self.term_data['target_term'])
|
|
625
|
+
self.priority_spin.setValue(self.term_data['priority'])
|
|
626
|
+
self.domain_edit.setText(self.term_data['domain'])
|
|
627
|
+
# Use note field if available, otherwise fall back to definition (legacy)
|
|
628
|
+
note_text = self.term_data['note'] or self.term_data['definition']
|
|
629
|
+
self.note_edit.setPlainText(note_text)
|
|
630
|
+
self.project_edit.setText(self.term_data['project'])
|
|
631
|
+
self.client_edit.setText(self.term_data['client'])
|
|
632
|
+
self.forbidden_check.setChecked(self.term_data['forbidden'])
|
|
633
|
+
|
|
634
|
+
# Load synonyms
|
|
635
|
+
self.load_synonyms()
|
|
636
|
+
|
|
637
|
+
except Exception as e:
|
|
638
|
+
QMessageBox.critical(self, "Error", f"Failed to load term data: {e}")
|
|
639
|
+
|
|
640
|
+
def load_synonyms(self):
|
|
641
|
+
"""Load synonyms for current term"""
|
|
642
|
+
if not self.db_manager or not self.term_id:
|
|
643
|
+
return
|
|
644
|
+
|
|
645
|
+
try:
|
|
646
|
+
cursor = self.db_manager.cursor
|
|
647
|
+
|
|
648
|
+
# Check if forbidden column exists (backward compatibility)
|
|
649
|
+
cursor.execute("PRAGMA table_info(termbase_synonyms)")
|
|
650
|
+
columns = [row[1] for row in cursor.fetchall()]
|
|
651
|
+
has_forbidden = 'forbidden' in columns
|
|
652
|
+
has_display_order = 'display_order' in columns
|
|
653
|
+
|
|
654
|
+
# Load source synonyms
|
|
655
|
+
if has_forbidden and has_display_order:
|
|
656
|
+
cursor.execute("""
|
|
657
|
+
SELECT synonym_text, forbidden FROM termbase_synonyms
|
|
658
|
+
WHERE term_id = ? AND language = 'source'
|
|
659
|
+
ORDER BY display_order ASC
|
|
660
|
+
""", (self.term_id,))
|
|
661
|
+
else:
|
|
662
|
+
cursor.execute("""
|
|
663
|
+
SELECT synonym_text FROM termbase_synonyms
|
|
664
|
+
WHERE term_id = ? AND language = 'source'
|
|
665
|
+
ORDER BY created_date ASC
|
|
666
|
+
""", (self.term_id,))
|
|
667
|
+
|
|
668
|
+
for row in cursor.fetchall():
|
|
669
|
+
text = row[0]
|
|
670
|
+
forbidden = bool(row[1]) if has_forbidden and len(row) > 1 else False
|
|
671
|
+
display = f"{'🚫 ' if forbidden else ''}{text}"
|
|
672
|
+
item = QListWidgetItem(display)
|
|
673
|
+
item.setData(Qt.ItemDataRole.UserRole, {'text': text, 'forbidden': forbidden})
|
|
674
|
+
if forbidden:
|
|
675
|
+
item.setForeground(QColor('#d32f2f'))
|
|
676
|
+
self.source_synonym_list.addItem(item)
|
|
677
|
+
|
|
678
|
+
# Load target synonyms
|
|
679
|
+
if has_forbidden and has_display_order:
|
|
680
|
+
cursor.execute("""
|
|
681
|
+
SELECT synonym_text, forbidden FROM termbase_synonyms
|
|
682
|
+
WHERE term_id = ? AND language = 'target'
|
|
683
|
+
ORDER BY display_order ASC
|
|
684
|
+
""", (self.term_id,))
|
|
685
|
+
else:
|
|
686
|
+
cursor.execute("""
|
|
687
|
+
SELECT synonym_text FROM termbase_synonyms
|
|
688
|
+
WHERE term_id = ? AND language = 'target'
|
|
689
|
+
ORDER BY created_date ASC
|
|
690
|
+
""", (self.term_id,))
|
|
691
|
+
|
|
692
|
+
for row in cursor.fetchall():
|
|
693
|
+
text = row[0]
|
|
694
|
+
forbidden = bool(row[1]) if has_forbidden and len(row) > 1 else False
|
|
695
|
+
display = f"{'🚫 ' if forbidden else ''}{text}"
|
|
696
|
+
item = QListWidgetItem(display)
|
|
697
|
+
item.setData(Qt.ItemDataRole.UserRole, {'text': text, 'forbidden': forbidden})
|
|
698
|
+
if forbidden:
|
|
699
|
+
item.setForeground(QColor('#d32f2f'))
|
|
700
|
+
self.target_synonym_list.addItem(item)
|
|
701
|
+
|
|
702
|
+
except Exception as e:
|
|
703
|
+
# Silently fail for backward compatibility
|
|
704
|
+
print(f"Warning: Could not load synonyms: {e}")
|
|
705
|
+
|
|
706
|
+
def delete_term(self):
|
|
707
|
+
"""Delete this term from database"""
|
|
708
|
+
if not self.db_manager or not self.term_id:
|
|
709
|
+
return
|
|
710
|
+
|
|
711
|
+
# Confirm deletion
|
|
712
|
+
reply = QMessageBox.question(
|
|
713
|
+
self,
|
|
714
|
+
"Confirm Deletion",
|
|
715
|
+
f"Delete this glossary entry?\n\nSource: {self.source_edit.text()}\nTarget: {self.target_edit.text()}\n\nThis action cannot be undone.",
|
|
716
|
+
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
|
|
717
|
+
QMessageBox.StandardButton.No
|
|
718
|
+
)
|
|
719
|
+
|
|
720
|
+
if reply == QMessageBox.StandardButton.Yes:
|
|
721
|
+
try:
|
|
722
|
+
cursor = self.db_manager.cursor
|
|
723
|
+
cursor.execute("DELETE FROM termbase_terms WHERE id = ?", (self.term_id,))
|
|
724
|
+
self.db_manager.connection.commit()
|
|
725
|
+
QMessageBox.information(self, "Success", "Glossary entry deleted")
|
|
726
|
+
self.accept() # Close dialog with success
|
|
727
|
+
except Exception as e:
|
|
728
|
+
QMessageBox.critical(self, "Error", f"Failed to delete entry: {e}")
|
|
729
|
+
|
|
730
|
+
def save_term(self):
|
|
731
|
+
"""Save term to database"""
|
|
732
|
+
# Validate inputs
|
|
733
|
+
source_term = self.source_edit.text().strip()
|
|
734
|
+
target_term = self.target_edit.text().strip()
|
|
735
|
+
|
|
736
|
+
if not source_term or not target_term:
|
|
737
|
+
QMessageBox.warning(
|
|
738
|
+
self,
|
|
739
|
+
"Validation Error",
|
|
740
|
+
"Both source and target terms are required."
|
|
741
|
+
)
|
|
742
|
+
return
|
|
743
|
+
|
|
744
|
+
if not self.db_manager:
|
|
745
|
+
QMessageBox.critical(
|
|
746
|
+
self,
|
|
747
|
+
"Error",
|
|
748
|
+
"No database connection available."
|
|
749
|
+
)
|
|
750
|
+
return
|
|
751
|
+
|
|
752
|
+
try:
|
|
753
|
+
cursor = self.db_manager.cursor
|
|
754
|
+
|
|
755
|
+
# Gather data
|
|
756
|
+
priority = self.priority_spin.value()
|
|
757
|
+
domain = self.domain_edit.text().strip()
|
|
758
|
+
note = self.note_edit.toPlainText().strip()
|
|
759
|
+
project = self.project_edit.text().strip()
|
|
760
|
+
client = self.client_edit.text().strip()
|
|
761
|
+
forbidden = self.forbidden_check.isChecked()
|
|
762
|
+
|
|
763
|
+
if self.term_id:
|
|
764
|
+
# Update existing term
|
|
765
|
+
cursor.execute("""
|
|
766
|
+
UPDATE termbase_terms
|
|
767
|
+
SET source_term = ?, target_term = ?, priority = ?,
|
|
768
|
+
domain = ?, notes = ?, project = ?, client = ?, forbidden = ?
|
|
769
|
+
WHERE id = ?
|
|
770
|
+
""", (source_term, target_term, priority, domain, note, project, client, forbidden, self.term_id))
|
|
771
|
+
else:
|
|
772
|
+
# Insert new term
|
|
773
|
+
cursor.execute("""
|
|
774
|
+
INSERT INTO termbase_terms
|
|
775
|
+
(termbase_id, source_term, target_term, priority, domain, notes, project, client, forbidden)
|
|
776
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
777
|
+
""", (self.termbase_id, source_term, target_term, priority, domain, note, project, client, forbidden))
|
|
778
|
+
|
|
779
|
+
self.db_manager.connection.commit()
|
|
780
|
+
|
|
781
|
+
# Save synonyms (get the term_id if this was a new term)
|
|
782
|
+
if not self.term_id:
|
|
783
|
+
self.term_id = cursor.lastrowid
|
|
784
|
+
|
|
785
|
+
self.save_synonyms()
|
|
786
|
+
|
|
787
|
+
# Success
|
|
788
|
+
self.accept()
|
|
789
|
+
|
|
790
|
+
except Exception as e:
|
|
791
|
+
QMessageBox.critical(
|
|
792
|
+
self,
|
|
793
|
+
"Error",
|
|
794
|
+
f"Failed to save term: {e}"
|
|
795
|
+
)
|
|
796
|
+
|
|
797
|
+
def save_synonyms(self):
|
|
798
|
+
"""Save synonyms to database"""
|
|
799
|
+
if not self.db_manager or not self.term_id:
|
|
800
|
+
return
|
|
801
|
+
|
|
802
|
+
try:
|
|
803
|
+
cursor = self.db_manager.cursor
|
|
804
|
+
|
|
805
|
+
# Delete existing synonyms for this term
|
|
806
|
+
cursor.execute("DELETE FROM termbase_synonyms WHERE term_id = ?", (self.term_id,))
|
|
807
|
+
|
|
808
|
+
# Save source synonyms
|
|
809
|
+
for i in range(self.source_synonym_list.count()):
|
|
810
|
+
item = self.source_synonym_list.item(i)
|
|
811
|
+
data = item.data(Qt.ItemDataRole.UserRole)
|
|
812
|
+
cursor.execute("""
|
|
813
|
+
INSERT INTO termbase_synonyms (term_id, synonym_text, language, display_order, forbidden)
|
|
814
|
+
VALUES (?, ?, 'source', ?, ?)
|
|
815
|
+
""", (self.term_id, data['text'], i, 1 if data['forbidden'] else 0))
|
|
816
|
+
|
|
817
|
+
# Save target synonyms
|
|
818
|
+
for i in range(self.target_synonym_list.count()):
|
|
819
|
+
item = self.target_synonym_list.item(i)
|
|
820
|
+
data = item.data(Qt.ItemDataRole.UserRole)
|
|
821
|
+
cursor.execute("""
|
|
822
|
+
INSERT INTO termbase_synonyms (term_id, synonym_text, language, display_order, forbidden)
|
|
823
|
+
VALUES (?, ?, 'target', ?, ?)
|
|
824
|
+
""", (self.term_id, data['text'], i, 1 if data['forbidden'] else 0))
|
|
825
|
+
|
|
826
|
+
self.db_manager.connection.commit()
|
|
827
|
+
|
|
828
|
+
except Exception as e:
|
|
829
|
+
QMessageBox.warning(self, "Warning", f"Failed to save synonyms: {e}")
|
|
830
|
+
|
|
831
|
+
def get_term_data(self) -> Optional[dict]:
|
|
832
|
+
"""Get the current term data from the form fields"""
|
|
833
|
+
return {
|
|
834
|
+
'source_term': self.source_edit.text().strip(),
|
|
835
|
+
'target_term': self.target_edit.text().strip(),
|
|
836
|
+
'priority': self.priority_spin.value(),
|
|
837
|
+
'domain': self.domain_edit.text().strip(),
|
|
838
|
+
'note': self.note_edit.toPlainText().strip(),
|
|
839
|
+
'project': self.project_edit.text().strip(),
|
|
840
|
+
'client': self.client_edit.text().strip(),
|
|
841
|
+
'forbidden': self.forbidden_check.isChecked()
|
|
842
|
+
}
|