supervertaler 1.9.109__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 +44945 -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 +1766 -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 +904 -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 +325 -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 +248 -0
- modules/tag_cleaner.py +260 -0
- modules/tag_manager.py +333 -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 +1161 -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 +670 -0
- modules/translation_results_panel.py +2134 -0
- modules/translation_services.py +282 -0
- modules/unified_prompt_library.py +656 -0
- modules/unified_prompt_manager_qt.py +3715 -0
- modules/voice_commands.py +920 -0
- modules/voice_dictation.py +477 -0
- modules/voice_dictation_lite.py +249 -0
- supervertaler-1.9.109.dist-info/METADATA +788 -0
- supervertaler-1.9.109.dist-info/RECORD +85 -0
- supervertaler-1.9.109.dist-info/WHEEL +5 -0
- supervertaler-1.9.109.dist-info/entry_points.txt +2 -0
- supervertaler-1.9.109.dist-info/licenses/LICENSE +21 -0
- supervertaler-1.9.109.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,481 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Encoding Repair Tool UI - Menu-based interface for the encoding repair module
|
|
3
|
+
|
|
4
|
+
Provides a user-friendly GUI for detecting and fixing text encoding corruption.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import tkinter as tk
|
|
8
|
+
from tkinter import ttk, filedialog, messagebox, scrolledtext
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
import threading
|
|
11
|
+
from modules.encoding_repair import EncodingRepair
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class EncodingRepairWindow:
|
|
15
|
+
"""GUI window for encoding repair operations."""
|
|
16
|
+
|
|
17
|
+
def __init__(self, parent, theme_colors=None):
|
|
18
|
+
"""
|
|
19
|
+
Initialize the encoding repair window.
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
parent: Parent tkinter widget
|
|
23
|
+
theme_colors: Optional dict with color scheme
|
|
24
|
+
"""
|
|
25
|
+
self.parent = parent
|
|
26
|
+
self.colors = theme_colors or {
|
|
27
|
+
'bg': '#f0f0f0',
|
|
28
|
+
'fg': '#333333',
|
|
29
|
+
'accent': '#0066cc',
|
|
30
|
+
'success': '#4CAF50',
|
|
31
|
+
'error': '#f44336',
|
|
32
|
+
'warning': '#ff9800',
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
self.window = tk.Toplevel(parent)
|
|
36
|
+
self.window.title("Text Encoding Repair Tool")
|
|
37
|
+
self.window.geometry("700x600")
|
|
38
|
+
self.window.resizable(True, True)
|
|
39
|
+
|
|
40
|
+
self._create_ui()
|
|
41
|
+
|
|
42
|
+
def _create_ui(self):
|
|
43
|
+
"""Create the user interface."""
|
|
44
|
+
# Main frame
|
|
45
|
+
main_frame = ttk.Frame(self.window)
|
|
46
|
+
main_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
|
|
47
|
+
|
|
48
|
+
# Title
|
|
49
|
+
title_label = ttk.Label(
|
|
50
|
+
main_frame,
|
|
51
|
+
text="Text Encoding Corruption Repair",
|
|
52
|
+
font=("Arial", 14, "bold")
|
|
53
|
+
)
|
|
54
|
+
title_label.pack(pady=(0, 10))
|
|
55
|
+
|
|
56
|
+
# Description
|
|
57
|
+
desc = ttk.Label(
|
|
58
|
+
main_frame,
|
|
59
|
+
text="Detects and fixes text encoding issues (mojibake) caused by UTF-8\n"
|
|
60
|
+
"being incorrectly decoded as Latin-1 or Windows-1252.",
|
|
61
|
+
font=("Arial", 9),
|
|
62
|
+
justify=tk.LEFT,
|
|
63
|
+
)
|
|
64
|
+
desc.pack(pady=(0, 15), fill=tk.X)
|
|
65
|
+
|
|
66
|
+
# File selection frame
|
|
67
|
+
file_frame = ttk.LabelFrame(main_frame, text="File Selection", padding=10)
|
|
68
|
+
file_frame.pack(fill=tk.X, pady=(0, 10))
|
|
69
|
+
|
|
70
|
+
button_frame = ttk.Frame(file_frame)
|
|
71
|
+
button_frame.pack(fill=tk.X)
|
|
72
|
+
|
|
73
|
+
self.file_path_var = tk.StringVar()
|
|
74
|
+
ttk.Button(
|
|
75
|
+
button_frame,
|
|
76
|
+
text="📂 Select File",
|
|
77
|
+
command=self._select_file
|
|
78
|
+
).pack(side=tk.LEFT, padx=(0, 10))
|
|
79
|
+
|
|
80
|
+
ttk.Button(
|
|
81
|
+
button_frame,
|
|
82
|
+
text="📁 Select Folder",
|
|
83
|
+
command=self._select_folder
|
|
84
|
+
).pack(side=tk.LEFT, padx=(0, 10))
|
|
85
|
+
|
|
86
|
+
# File path display
|
|
87
|
+
self.path_label = ttk.Label(
|
|
88
|
+
file_frame,
|
|
89
|
+
text="No file selected",
|
|
90
|
+
font=("Arial", 9),
|
|
91
|
+
foreground="#666666"
|
|
92
|
+
)
|
|
93
|
+
self.path_label.pack(pady=(10, 0), fill=tk.X)
|
|
94
|
+
|
|
95
|
+
# Action buttons frame
|
|
96
|
+
action_frame = ttk.LabelFrame(main_frame, text="Actions", padding=10)
|
|
97
|
+
action_frame.pack(fill=tk.X, pady=(0, 10))
|
|
98
|
+
|
|
99
|
+
button_row1 = ttk.Frame(action_frame)
|
|
100
|
+
button_row1.pack(fill=tk.X, pady=(0, 10))
|
|
101
|
+
|
|
102
|
+
ttk.Button(
|
|
103
|
+
button_row1,
|
|
104
|
+
text="🔍 Scan File",
|
|
105
|
+
command=self._scan_file,
|
|
106
|
+
width=20
|
|
107
|
+
).pack(side=tk.LEFT, padx=(0, 10))
|
|
108
|
+
|
|
109
|
+
ttk.Button(
|
|
110
|
+
button_row1,
|
|
111
|
+
text="🔧 Repair File",
|
|
112
|
+
command=self._repair_file,
|
|
113
|
+
width=20
|
|
114
|
+
).pack(side=tk.LEFT, padx=(0, 10))
|
|
115
|
+
|
|
116
|
+
button_row2 = ttk.Frame(action_frame)
|
|
117
|
+
button_row2.pack(fill=tk.X)
|
|
118
|
+
|
|
119
|
+
ttk.Button(
|
|
120
|
+
button_row2,
|
|
121
|
+
text="📂 Scan Folder",
|
|
122
|
+
command=self._scan_folder,
|
|
123
|
+
width=20
|
|
124
|
+
).pack(side=tk.LEFT, padx=(0, 10))
|
|
125
|
+
|
|
126
|
+
ttk.Button(
|
|
127
|
+
button_row2,
|
|
128
|
+
text="🔧 Repair Folder",
|
|
129
|
+
command=self._repair_folder,
|
|
130
|
+
width=20
|
|
131
|
+
).pack(side=tk.LEFT, padx=(0, 10))
|
|
132
|
+
|
|
133
|
+
# Results frame
|
|
134
|
+
results_frame = ttk.LabelFrame(main_frame, text="Results", padding=10)
|
|
135
|
+
results_frame.pack(fill=tk.BOTH, expand=True, pady=(0, 10))
|
|
136
|
+
|
|
137
|
+
self.results_text = scrolledtext.ScrolledText(
|
|
138
|
+
results_frame,
|
|
139
|
+
height=12,
|
|
140
|
+
font=("Courier New", 9),
|
|
141
|
+
bg="white",
|
|
142
|
+
fg="#333333",
|
|
143
|
+
)
|
|
144
|
+
self.results_text.pack(fill=tk.BOTH, expand=True)
|
|
145
|
+
|
|
146
|
+
# Add tags for colorization
|
|
147
|
+
self.results_text.tag_config("success", foreground="#4CAF50", font=("Courier New", 9, "bold"))
|
|
148
|
+
self.results_text.tag_config("error", foreground="#f44336", font=("Courier New", 9, "bold"))
|
|
149
|
+
self.results_text.tag_config("warning", foreground="#ff9800", font=("Courier New", 9, "bold"))
|
|
150
|
+
self.results_text.tag_config("info", foreground="#0066cc")
|
|
151
|
+
|
|
152
|
+
# Status bar
|
|
153
|
+
self.status_var = tk.StringVar(value="Ready")
|
|
154
|
+
status_bar = ttk.Label(
|
|
155
|
+
main_frame,
|
|
156
|
+
textvariable=self.status_var,
|
|
157
|
+
font=("Arial", 8),
|
|
158
|
+
foreground="#666666"
|
|
159
|
+
)
|
|
160
|
+
status_bar.pack(fill=tk.X)
|
|
161
|
+
|
|
162
|
+
def _select_file(self):
|
|
163
|
+
"""Open file selection dialog."""
|
|
164
|
+
file_path = filedialog.askopenfilename(
|
|
165
|
+
title="Select a text file",
|
|
166
|
+
filetypes=[
|
|
167
|
+
("Text files", "*.txt"),
|
|
168
|
+
("CSV files", "*.csv"),
|
|
169
|
+
("TSV files", "*.tsv"),
|
|
170
|
+
("Markdown files", "*.md"),
|
|
171
|
+
("All files", "*.*"),
|
|
172
|
+
]
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
if file_path:
|
|
176
|
+
self.file_path_var.set(file_path)
|
|
177
|
+
self.path_label.config(text=f"📄 {Path(file_path).name}")
|
|
178
|
+
self._update_status(f"Selected: {Path(file_path).name}")
|
|
179
|
+
|
|
180
|
+
def _select_folder(self):
|
|
181
|
+
"""Open folder selection dialog."""
|
|
182
|
+
folder_path = filedialog.askdirectory(title="Select a folder")
|
|
183
|
+
|
|
184
|
+
if folder_path:
|
|
185
|
+
self.file_path_var.set(folder_path)
|
|
186
|
+
self.path_label.config(text=f"📁 {Path(folder_path).name}")
|
|
187
|
+
self._update_status(f"Selected: {Path(folder_path).name}")
|
|
188
|
+
|
|
189
|
+
def _scan_file(self):
|
|
190
|
+
"""Scan a single file for encoding issues."""
|
|
191
|
+
file_path = self.file_path_var.get()
|
|
192
|
+
|
|
193
|
+
if not file_path:
|
|
194
|
+
messagebox.showwarning("No file selected", "Please select a file first.")
|
|
195
|
+
return
|
|
196
|
+
|
|
197
|
+
self._clear_results()
|
|
198
|
+
self._update_status("Scanning file...")
|
|
199
|
+
|
|
200
|
+
def scan():
|
|
201
|
+
try:
|
|
202
|
+
with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
|
|
203
|
+
content = f.read()
|
|
204
|
+
|
|
205
|
+
has_corruption, count, patterns = EncodingRepair.detect_corruption(content)
|
|
206
|
+
|
|
207
|
+
# Display results
|
|
208
|
+
self.results_text.insert(tk.END, f"File: {Path(file_path).name}\n", "info")
|
|
209
|
+
self.results_text.insert(tk.END, f"Path: {file_path}\n", "info")
|
|
210
|
+
self.results_text.insert(tk.END, f"Size: {len(content):,} characters\n\n", "info")
|
|
211
|
+
|
|
212
|
+
if has_corruption:
|
|
213
|
+
self.results_text.insert(
|
|
214
|
+
tk.END,
|
|
215
|
+
f"⚠️ ENCODING CORRUPTION DETECTED\n",
|
|
216
|
+
"warning"
|
|
217
|
+
)
|
|
218
|
+
self.results_text.insert(tk.END, f"Total corruptions: {count}\n\n", "warning")
|
|
219
|
+
self.results_text.insert(tk.END, "Patterns found:\n", "warning")
|
|
220
|
+
|
|
221
|
+
for i, pattern in enumerate(patterns, 1):
|
|
222
|
+
self.results_text.insert(tk.END, f" {i}. {pattern}\n")
|
|
223
|
+
|
|
224
|
+
self.results_text.insert(
|
|
225
|
+
tk.END,
|
|
226
|
+
"\n✅ You can repair this file using the 'Repair File' button.\n",
|
|
227
|
+
"success"
|
|
228
|
+
)
|
|
229
|
+
else:
|
|
230
|
+
self.results_text.insert(
|
|
231
|
+
tk.END,
|
|
232
|
+
"✅ NO ENCODING CORRUPTION DETECTED\n",
|
|
233
|
+
"success"
|
|
234
|
+
)
|
|
235
|
+
self.results_text.insert(
|
|
236
|
+
tk.END,
|
|
237
|
+
"This file appears to be properly encoded.\n",
|
|
238
|
+
"success"
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
self._update_status("Scan complete")
|
|
242
|
+
|
|
243
|
+
except Exception as e:
|
|
244
|
+
self.results_text.insert(tk.END, f"❌ Error: {str(e)}\n", "error")
|
|
245
|
+
self._update_status("Scan failed")
|
|
246
|
+
|
|
247
|
+
# Run in background thread
|
|
248
|
+
thread = threading.Thread(target=scan, daemon=True)
|
|
249
|
+
thread.start()
|
|
250
|
+
|
|
251
|
+
def _repair_file(self):
|
|
252
|
+
"""Repair encoding issues in a single file."""
|
|
253
|
+
file_path = self.file_path_var.get()
|
|
254
|
+
|
|
255
|
+
if not file_path:
|
|
256
|
+
messagebox.showwarning("No file selected", "Please select a file first.")
|
|
257
|
+
return
|
|
258
|
+
|
|
259
|
+
if not Path(file_path).is_file():
|
|
260
|
+
messagebox.showerror("Invalid file", "The selected path is not a file.")
|
|
261
|
+
return
|
|
262
|
+
|
|
263
|
+
# Confirm before repair
|
|
264
|
+
if not messagebox.askyesno("Confirm Repair", f"Repair {Path(file_path).name}?\n\nA backup will be created."):
|
|
265
|
+
return
|
|
266
|
+
|
|
267
|
+
self._clear_results()
|
|
268
|
+
self._update_status("Repairing file...")
|
|
269
|
+
|
|
270
|
+
def repair():
|
|
271
|
+
try:
|
|
272
|
+
# Create backup
|
|
273
|
+
backup_path = f"{file_path}.backup"
|
|
274
|
+
Path(file_path).read_text(encoding='utf-8', errors='ignore')
|
|
275
|
+
with open(file_path, 'rb') as f_src:
|
|
276
|
+
with open(backup_path, 'wb') as f_dst:
|
|
277
|
+
f_dst.write(f_src.read())
|
|
278
|
+
|
|
279
|
+
# Repair
|
|
280
|
+
success, message, info = EncodingRepair.repair_with_encoding_fallback(file_path)
|
|
281
|
+
|
|
282
|
+
if success:
|
|
283
|
+
self.results_text.insert(tk.END, message + "\n\n", "success")
|
|
284
|
+
self.results_text.insert(tk.END, f"Backup created: {backup_path}\n", "info")
|
|
285
|
+
messagebox.showinfo("Success", f"File repaired successfully!\n\n{message}")
|
|
286
|
+
self._update_status("File repaired successfully")
|
|
287
|
+
else:
|
|
288
|
+
self.results_text.insert(tk.END, message + "\n", "error")
|
|
289
|
+
self._update_status("Repair failed")
|
|
290
|
+
|
|
291
|
+
except Exception as e:
|
|
292
|
+
error_msg = f"❌ Error during repair: {str(e)}\n"
|
|
293
|
+
self.results_text.insert(tk.END, error_msg, "error")
|
|
294
|
+
messagebox.showerror("Repair Failed", error_msg)
|
|
295
|
+
self._update_status("Repair failed")
|
|
296
|
+
|
|
297
|
+
# Run in background thread
|
|
298
|
+
thread = threading.Thread(target=repair, daemon=True)
|
|
299
|
+
thread.start()
|
|
300
|
+
|
|
301
|
+
def _scan_folder(self):
|
|
302
|
+
"""Scan a folder for files with encoding issues."""
|
|
303
|
+
folder_path = self.file_path_var.get()
|
|
304
|
+
|
|
305
|
+
if not folder_path:
|
|
306
|
+
messagebox.showwarning("No folder selected", "Please select a folder first.")
|
|
307
|
+
return
|
|
308
|
+
|
|
309
|
+
if not Path(folder_path).is_dir():
|
|
310
|
+
messagebox.showerror("Invalid folder", "The selected path is not a folder.")
|
|
311
|
+
return
|
|
312
|
+
|
|
313
|
+
self._clear_results()
|
|
314
|
+
self._update_status("Scanning folder...")
|
|
315
|
+
|
|
316
|
+
def scan():
|
|
317
|
+
try:
|
|
318
|
+
results = EncodingRepair.scan_directory(folder_path)
|
|
319
|
+
|
|
320
|
+
self.results_text.insert(tk.END, f"Folder: {Path(folder_path).name}\n", "info")
|
|
321
|
+
self.results_text.insert(tk.END, f"Path: {folder_path}\n\n", "info")
|
|
322
|
+
self.results_text.insert(tk.END, f"Files scanned: {results['files_scanned']}\n", "info")
|
|
323
|
+
|
|
324
|
+
if results['files_with_corruption']:
|
|
325
|
+
self.results_text.insert(
|
|
326
|
+
tk.END,
|
|
327
|
+
f"⚠️ Files with corruption: {len(results['files_with_corruption'])}\n",
|
|
328
|
+
"warning"
|
|
329
|
+
)
|
|
330
|
+
self.results_text.insert(
|
|
331
|
+
tk.END,
|
|
332
|
+
f"Total corruptions found: {results['total_corruptions']}\n\n",
|
|
333
|
+
"warning"
|
|
334
|
+
)
|
|
335
|
+
|
|
336
|
+
for file_info in results['files_with_corruption']:
|
|
337
|
+
self.results_text.insert(
|
|
338
|
+
tk.END,
|
|
339
|
+
f"📄 {Path(file_info['file']).name}\n",
|
|
340
|
+
"warning"
|
|
341
|
+
)
|
|
342
|
+
self.results_text.insert(
|
|
343
|
+
tk.END,
|
|
344
|
+
f" Corruptions: {file_info['corruptions']}\n",
|
|
345
|
+
"info"
|
|
346
|
+
)
|
|
347
|
+
for pattern in file_info['patterns']:
|
|
348
|
+
self.results_text.insert(tk.END, f" • {pattern}\n", "info")
|
|
349
|
+
self.results_text.insert(tk.END, "\n")
|
|
350
|
+
else:
|
|
351
|
+
self.results_text.insert(
|
|
352
|
+
tk.END,
|
|
353
|
+
"✅ NO ENCODING CORRUPTION DETECTED\n",
|
|
354
|
+
"success"
|
|
355
|
+
)
|
|
356
|
+
self.results_text.insert(
|
|
357
|
+
tk.END,
|
|
358
|
+
"All files in this folder are properly encoded.\n",
|
|
359
|
+
"success"
|
|
360
|
+
)
|
|
361
|
+
|
|
362
|
+
self._update_status("Folder scan complete")
|
|
363
|
+
|
|
364
|
+
except Exception as e:
|
|
365
|
+
self.results_text.insert(tk.END, f"❌ Error: {str(e)}\n", "error")
|
|
366
|
+
self._update_status("Scan failed")
|
|
367
|
+
|
|
368
|
+
# Run in background thread
|
|
369
|
+
thread = threading.Thread(target=scan, daemon=True)
|
|
370
|
+
thread.start()
|
|
371
|
+
|
|
372
|
+
def _repair_folder(self):
|
|
373
|
+
"""Repair encoding issues in all files in a folder."""
|
|
374
|
+
folder_path = self.file_path_var.get()
|
|
375
|
+
|
|
376
|
+
if not folder_path:
|
|
377
|
+
messagebox.showwarning("No folder selected", "Please select a folder first.")
|
|
378
|
+
return
|
|
379
|
+
|
|
380
|
+
if not Path(folder_path).is_dir():
|
|
381
|
+
messagebox.showerror("Invalid folder", "The selected path is not a folder.")
|
|
382
|
+
return
|
|
383
|
+
|
|
384
|
+
# First scan to show what will be repaired
|
|
385
|
+
results = EncodingRepair.scan_directory(folder_path)
|
|
386
|
+
|
|
387
|
+
if not results['files_with_corruption']:
|
|
388
|
+
messagebox.showinfo("No issues found", "No encoding corruption detected in this folder.")
|
|
389
|
+
return
|
|
390
|
+
|
|
391
|
+
files_to_repair = len(results['files_with_corruption'])
|
|
392
|
+
if not messagebox.askyesno(
|
|
393
|
+
"Confirm Repair",
|
|
394
|
+
f"Repair {files_to_repair} file(s) with encoding issues?\n\nBackups will be created."
|
|
395
|
+
):
|
|
396
|
+
return
|
|
397
|
+
|
|
398
|
+
self._clear_results()
|
|
399
|
+
self._update_status("Repairing folder...")
|
|
400
|
+
|
|
401
|
+
def repair():
|
|
402
|
+
repaired_count = 0
|
|
403
|
+
failed_count = 0
|
|
404
|
+
|
|
405
|
+
self.results_text.insert(tk.END, f"Repairing {files_to_repair} file(s)...\n\n", "info")
|
|
406
|
+
|
|
407
|
+
for file_info in results['files_with_corruption']:
|
|
408
|
+
file_path = file_info['file']
|
|
409
|
+
try:
|
|
410
|
+
# Create backup
|
|
411
|
+
backup_path = f"{file_path}.backup"
|
|
412
|
+
with open(file_path, 'rb') as f_src:
|
|
413
|
+
with open(backup_path, 'wb') as f_dst:
|
|
414
|
+
f_dst.write(f_src.read())
|
|
415
|
+
|
|
416
|
+
# Repair
|
|
417
|
+
success, message, _ = EncodingRepair.repair_with_encoding_fallback(file_path)
|
|
418
|
+
|
|
419
|
+
if success:
|
|
420
|
+
self.results_text.insert(
|
|
421
|
+
tk.END,
|
|
422
|
+
f"✅ {Path(file_path).name}\n",
|
|
423
|
+
"success"
|
|
424
|
+
)
|
|
425
|
+
repaired_count += 1
|
|
426
|
+
else:
|
|
427
|
+
self.results_text.insert(
|
|
428
|
+
tk.END,
|
|
429
|
+
f"❌ {Path(file_path).name}: {message}\n",
|
|
430
|
+
"error"
|
|
431
|
+
)
|
|
432
|
+
failed_count += 1
|
|
433
|
+
|
|
434
|
+
except Exception as e:
|
|
435
|
+
self.results_text.insert(
|
|
436
|
+
tk.END,
|
|
437
|
+
f"❌ {Path(file_path).name}: {str(e)}\n",
|
|
438
|
+
"error"
|
|
439
|
+
)
|
|
440
|
+
failed_count += 1
|
|
441
|
+
|
|
442
|
+
# Summary
|
|
443
|
+
self.results_text.insert(tk.END, f"\n{'='*50}\n", "info")
|
|
444
|
+
self.results_text.insert(
|
|
445
|
+
tk.END,
|
|
446
|
+
f"Repair complete: {repaired_count} succeeded, {failed_count} failed\n",
|
|
447
|
+
"success" if failed_count == 0 else "warning"
|
|
448
|
+
)
|
|
449
|
+
|
|
450
|
+
self._update_status("Folder repair complete")
|
|
451
|
+
messagebox.showinfo(
|
|
452
|
+
"Repair Complete",
|
|
453
|
+
f"Repaired {repaired_count} file(s)\n"
|
|
454
|
+
f"Failed: {failed_count} file(s)"
|
|
455
|
+
)
|
|
456
|
+
|
|
457
|
+
# Run in background thread
|
|
458
|
+
thread = threading.Thread(target=repair, daemon=True)
|
|
459
|
+
thread.start()
|
|
460
|
+
|
|
461
|
+
def _clear_results(self):
|
|
462
|
+
"""Clear the results text area."""
|
|
463
|
+
self.results_text.config(state=tk.NORMAL)
|
|
464
|
+
self.results_text.delete(1.0, tk.END)
|
|
465
|
+
|
|
466
|
+
def _update_status(self, message: str):
|
|
467
|
+
"""Update the status bar."""
|
|
468
|
+
self.status_var.set(message)
|
|
469
|
+
self.window.update()
|
|
470
|
+
|
|
471
|
+
|
|
472
|
+
def open_encoding_repair_tool(parent, theme_colors=None):
|
|
473
|
+
"""
|
|
474
|
+
Open the encoding repair tool window.
|
|
475
|
+
|
|
476
|
+
Args:
|
|
477
|
+
parent: Parent tkinter widget
|
|
478
|
+
theme_colors: Optional dict with color scheme
|
|
479
|
+
"""
|
|
480
|
+
window = EncodingRepairWindow(parent, theme_colors)
|
|
481
|
+
return window
|