diff --git a/gui/config_integration.py b/gui/config_integration.py
index 1f02c7c..bcc181e 100644
--- a/gui/config_integration.py
+++ b/gui/config_integration.py
@@ -27,11 +27,6 @@ class ConfigIntegration:
with open(module_path, 'r', encoding='utf-8') as f:
content = f.read()
- # Create backup
- backup_path = module_path.with_suffix('.py.backup')
- with open(backup_path, 'w', encoding='utf-8') as f:
- f.write(content)
-
# Update configuration values
updated_content = self._update_config_values(content, config)
@@ -42,12 +37,6 @@ class ConfigIntegration:
return True
except Exception as e:
- # Restore from backup if something went wrong
- if backup_path.exists():
- with open(backup_path, 'r', encoding='utf-8') as f:
- content = f.read()
- with open(module_path, 'w', encoding='utf-8') as f:
- f.write(content)
raise e
def _update_config_values(self, content: str, config: Dict[str, Any]) -> str:
diff --git a/gui/file_manager_tab.py b/gui/file_manager_tab.py
new file mode 100644
index 0000000..10a9788
--- /dev/null
+++ b/gui/file_manager_tab.py
@@ -0,0 +1,466 @@
+#!/usr/bin/env python3
+"""
+File Manager Tab for DazedMTLTool GUI
+
+Manages input files from 'files' directory and output to 'translated' directory.
+Users can add files through GUI or by copying them to the files folder.
+"""
+
+import os
+import shutil
+from pathlib import Path
+from PyQt5.QtWidgets import (
+ QWidget, QVBoxLayout, QHBoxLayout, QLabel, QTreeWidget, QTreeWidgetItem,
+ QPushButton, QGroupBox, QSplitter, QTextEdit, QFileDialog, QMessageBox,
+ QProgressBar, QFrame, QHeaderView
+)
+from PyQt5.QtCore import Qt, QTimer
+from PyQt5.QtGui import QFont
+
+
+class FileManagerTab(QWidget):
+ """File Manager tab for managing input and output files."""
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self.project_root = Path(__file__).parent.parent
+ self.files_dir = self.project_root / "files"
+ self.translated_dir = self.project_root / "translated"
+
+ # Ensure directories exist
+ self.files_dir.mkdir(exist_ok=True)
+ self.translated_dir.mkdir(exist_ok=True)
+
+ self.setup_ui()
+ self.refresh_file_lists()
+
+ # Auto-refresh timer
+ self.refresh_timer = QTimer()
+ self.refresh_timer.timeout.connect(self.refresh_file_lists)
+ self.refresh_timer.start(3000) # Refresh every 3 seconds
+
+ def setup_ui(self):
+ """Set up the user interface."""
+ layout = QVBoxLayout()
+
+ # Title
+ title_label = QLabel("File Manager")
+ title_label.setStyleSheet("font-size: 16px; font-weight: bold; color: #007acc;")
+ layout.addWidget(title_label)
+
+ # Description
+ desc_label = QLabel(
+ "Input files are loaded from the 'files' directory. "
+ "Translated files will be saved to the 'translated' directory. "
+ "You can add files using the GUI or by copying them to the files folder."
+ )
+ desc_label.setWordWrap(True)
+ desc_label.setStyleSheet("color: #cccccc; margin-bottom: 10px;")
+ layout.addWidget(desc_label)
+
+ # Main splitter
+ splitter = QSplitter(Qt.Horizontal)
+
+ # Input files section
+ input_group = self.create_input_files_section()
+ splitter.addWidget(input_group)
+
+ # Output files section
+ output_group = self.create_output_files_section()
+ splitter.addWidget(output_group)
+
+ # Set equal sizes
+ splitter.setSizes([400, 400])
+ layout.addWidget(splitter)
+
+ # Action buttons
+ button_layout = self.create_action_buttons()
+ layout.addLayout(button_layout)
+
+ # Status bar
+ self.status_label = QLabel("Ready")
+ self.status_label.setStyleSheet("color: #cccccc; padding: 5px;")
+ layout.addWidget(self.status_label)
+
+ self.setLayout(layout)
+
+ def create_input_files_section(self):
+ """Create the input files section."""
+ group = QGroupBox("Input Files (files directory)")
+ layout = QVBoxLayout()
+
+ # File tree
+ self.input_tree = QTreeWidget()
+ self.input_tree.setHeaderLabels(["Filename", "Size", "Modified"])
+ self.input_tree.header().setSectionResizeMode(0, QHeaderView.Stretch)
+ self.input_tree.header().setSectionResizeMode(1, QHeaderView.ResizeToContents)
+ self.input_tree.header().setSectionResizeMode(2, QHeaderView.ResizeToContents)
+ self.input_tree.itemSelectionChanged.connect(self.on_input_file_selected)
+ layout.addWidget(self.input_tree)
+
+ # Input buttons
+ input_button_layout = QHBoxLayout()
+
+ self.add_file_btn = QPushButton("Add Files")
+ self.add_file_btn.clicked.connect(self.add_files)
+ input_button_layout.addWidget(self.add_file_btn)
+
+ self.remove_file_btn = QPushButton("Remove Selected")
+ self.remove_file_btn.clicked.connect(self.remove_input_file)
+ self.remove_file_btn.setEnabled(False)
+ input_button_layout.addWidget(self.remove_file_btn)
+
+ self.open_files_folder_btn = QPushButton("Open Files Folder")
+ self.open_files_folder_btn.clicked.connect(self.open_files_folder)
+ input_button_layout.addWidget(self.open_files_folder_btn)
+
+ input_button_layout.addStretch()
+ layout.addLayout(input_button_layout)
+
+ group.setLayout(layout)
+ return group
+
+ def create_output_files_section(self):
+ """Create the output files section."""
+ group = QGroupBox("Output Files (translated directory)")
+ layout = QVBoxLayout()
+
+ # File tree
+ self.output_tree = QTreeWidget()
+ self.output_tree.setHeaderLabels(["Filename", "Size", "Modified"])
+ self.output_tree.header().setSectionResizeMode(0, QHeaderView.Stretch)
+ self.output_tree.header().setSectionResizeMode(1, QHeaderView.ResizeToContents)
+ self.output_tree.header().setSectionResizeMode(2, QHeaderView.ResizeToContents)
+ self.output_tree.itemSelectionChanged.connect(self.on_output_file_selected)
+ layout.addWidget(self.output_tree)
+
+ # Output buttons
+ output_button_layout = QHBoxLayout()
+
+ self.view_output_btn = QPushButton("View Selected")
+ self.view_output_btn.clicked.connect(self.view_output_file)
+ self.view_output_btn.setEnabled(False)
+ output_button_layout.addWidget(self.view_output_btn)
+
+ self.delete_output_btn = QPushButton("Delete Selected")
+ self.delete_output_btn.clicked.connect(self.delete_output_file)
+ self.delete_output_btn.setEnabled(False)
+ output_button_layout.addWidget(self.delete_output_btn)
+
+ self.open_translated_folder_btn = QPushButton("Open Translated Folder")
+ self.open_translated_folder_btn.clicked.connect(self.open_translated_folder)
+ output_button_layout.addWidget(self.open_translated_folder_btn)
+
+ output_button_layout.addStretch()
+ layout.addLayout(output_button_layout)
+
+ group.setLayout(layout)
+ return group
+
+ def create_action_buttons(self):
+ """Create main action buttons."""
+ layout = QHBoxLayout()
+
+ self.refresh_btn = QPushButton("Refresh File Lists")
+ self.refresh_btn.clicked.connect(self.refresh_file_lists)
+ layout.addWidget(self.refresh_btn)
+
+ self.clear_translated_btn = QPushButton("Clear All Translated Files")
+ self.clear_translated_btn.clicked.connect(self.clear_translated_files)
+ layout.addWidget(self.clear_translated_btn)
+
+ layout.addStretch()
+
+ # Auto-refresh indicator
+ self.auto_refresh_label = QLabel("Auto-refresh: ON")
+ self.auto_refresh_label.setStyleSheet("color: #00aa00; font-size: 10px;")
+ layout.addWidget(self.auto_refresh_label)
+
+ return layout
+
+ def refresh_file_lists(self):
+ """Refresh both input and output file lists."""
+ self.refresh_input_files()
+ self.refresh_output_files()
+ self.update_status()
+
+ def refresh_input_files(self):
+ """Refresh the input files tree."""
+ self.input_tree.clear()
+
+ if not self.files_dir.exists():
+ return
+
+ supported_extensions = {'.json', '.txt', '.csv', '.js', '.py', '.xml', '.html', '.md'}
+
+ for file_path in self.files_dir.iterdir():
+ if (file_path.is_file() and
+ file_path.suffix.lower() in supported_extensions and
+ file_path.name != '.gitkeep'):
+ item = QTreeWidgetItem()
+ item.setText(0, file_path.name)
+
+ # File size
+ size = file_path.stat().st_size
+ if size < 1024:
+ size_str = f"{size} B"
+ elif size < 1024 * 1024:
+ size_str = f"{size / 1024:.1f} KB"
+ else:
+ size_str = f"{size / (1024 * 1024):.1f} MB"
+ item.setText(1, size_str)
+
+ # Modified time
+ import datetime
+ mtime = datetime.datetime.fromtimestamp(file_path.stat().st_mtime)
+ item.setText(2, mtime.strftime("%Y-%m-%d %H:%M"))
+
+ # Store file path
+ item.setData(0, Qt.UserRole, str(file_path))
+
+ self.input_tree.addTopLevelItem(item)
+
+ def refresh_output_files(self):
+ """Refresh the output files tree."""
+ self.output_tree.clear()
+
+ if not self.translated_dir.exists():
+ return
+
+ for file_path in self.translated_dir.iterdir():
+ if file_path.is_file() and file_path.name != '.gitkeep':
+ item = QTreeWidgetItem()
+ item.setText(0, file_path.name)
+
+ # File size
+ size = file_path.stat().st_size
+ if size < 1024:
+ size_str = f"{size} B"
+ elif size < 1024 * 1024:
+ size_str = f"{size / 1024:.1f} KB"
+ else:
+ size_str = f"{size / (1024 * 1024):.1f} MB"
+ item.setText(1, size_str)
+
+ # Modified time
+ import datetime
+ mtime = datetime.datetime.fromtimestamp(file_path.stat().st_mtime)
+ item.setText(2, mtime.strftime("%Y-%m-%d %H:%M"))
+
+ # Store file path
+ item.setData(0, Qt.UserRole, str(file_path))
+
+ self.output_tree.addTopLevelItem(item)
+
+ def update_status(self):
+ """Update the status label."""
+ input_count = self.input_tree.topLevelItemCount()
+ output_count = self.output_tree.topLevelItemCount()
+ self.status_label.setText(f"Input files: {input_count} | Output files: {output_count}")
+
+ def on_input_file_selected(self):
+ """Handle input file selection."""
+ selected = self.input_tree.selectedItems()
+ self.remove_file_btn.setEnabled(len(selected) > 0)
+
+ def on_output_file_selected(self):
+ """Handle output file selection."""
+ selected = self.output_tree.selectedItems()
+ self.view_output_btn.setEnabled(len(selected) > 0)
+ self.delete_output_btn.setEnabled(len(selected) > 0)
+
+ def add_files(self):
+ """Add files to the input directory."""
+ file_paths, _ = QFileDialog.getOpenFileNames(
+ self,
+ "Select Files to Add",
+ "",
+ "All Supported Files (*.json *.txt *.csv *.js *.py *.xml *.html *.md);;All Files (*.*)"
+ )
+
+ if not file_paths:
+ return
+
+ copied_count = 0
+ for file_path in file_paths:
+ source_path = Path(file_path)
+ dest_path = self.files_dir / source_path.name
+
+ try:
+ # Check if file already exists
+ if dest_path.exists():
+ reply = QMessageBox.question(
+ self,
+ "File Exists",
+ f"'{source_path.name}' already exists. Overwrite?",
+ QMessageBox.Yes | QMessageBox.No
+ )
+ if reply != QMessageBox.Yes:
+ continue
+
+ shutil.copy2(source_path, dest_path)
+ copied_count += 1
+
+ except Exception as e:
+ QMessageBox.warning(
+ self,
+ "Copy Error",
+ f"Failed to copy '{source_path.name}':\n{str(e)}"
+ )
+
+ if copied_count > 0:
+ QMessageBox.information(
+ self,
+ "Files Added",
+ f"Successfully added {copied_count} file(s) to the input directory."
+ )
+ self.refresh_input_files()
+
+ def remove_input_file(self):
+ """Remove selected input file."""
+ selected = self.input_tree.selectedItems()
+ if not selected:
+ return
+
+ item = selected[0]
+ file_path = Path(item.data(0, Qt.UserRole))
+
+ reply = QMessageBox.question(
+ self,
+ "Confirm Removal",
+ f"Are you sure you want to remove '{file_path.name}' from the input directory?",
+ QMessageBox.Yes | QMessageBox.No
+ )
+
+ if reply == QMessageBox.Yes:
+ try:
+ file_path.unlink()
+ self.refresh_input_files()
+ QMessageBox.information(self, "File Removed", f"'{file_path.name}' has been removed.")
+ except Exception as e:
+ QMessageBox.warning(self, "Error", f"Failed to remove file:\n{str(e)}")
+
+ def view_output_file(self):
+ """View selected output file."""
+ selected = self.output_tree.selectedItems()
+ if not selected:
+ return
+
+ item = selected[0]
+ file_path = Path(item.data(0, Qt.UserRole))
+
+ try:
+ # Open file with default system application
+ import subprocess
+ import platform
+
+ if platform.system() == 'Windows':
+ os.startfile(str(file_path))
+ elif platform.system() == 'Darwin': # macOS
+ subprocess.run(['open', str(file_path)])
+ else: # Linux
+ subprocess.run(['xdg-open', str(file_path)])
+
+ except Exception as e:
+ QMessageBox.warning(self, "Error", f"Failed to open file:\n{str(e)}")
+
+ def delete_output_file(self):
+ """Delete selected output file."""
+ selected = self.output_tree.selectedItems()
+ if not selected:
+ return
+
+ item = selected[0]
+ file_path = Path(item.data(0, Qt.UserRole))
+
+ reply = QMessageBox.question(
+ self,
+ "Confirm Deletion",
+ f"Are you sure you want to delete '{file_path.name}' from the output directory?",
+ QMessageBox.Yes | QMessageBox.No
+ )
+
+ if reply == QMessageBox.Yes:
+ try:
+ file_path.unlink()
+ self.refresh_output_files()
+ QMessageBox.information(self, "File Deleted", f"'{file_path.name}' has been deleted.")
+ except Exception as e:
+ QMessageBox.warning(self, "Error", f"Failed to delete file:\n{str(e)}")
+
+ def clear_translated_files(self):
+ """Clear all files from the translated directory."""
+ if self.output_tree.topLevelItemCount() == 0:
+ QMessageBox.information(self, "No Files", "There are no files to clear.")
+ return
+
+ reply = QMessageBox.question(
+ self,
+ "Confirm Clear All",
+ "Are you sure you want to delete ALL files from the translated directory?",
+ QMessageBox.Yes | QMessageBox.No
+ )
+
+ if reply == QMessageBox.Yes:
+ try:
+ deleted_count = 0
+ for file_path in self.translated_dir.iterdir():
+ if file_path.is_file() and file_path.name != '.gitkeep':
+ file_path.unlink()
+ deleted_count += 1
+
+ self.refresh_output_files()
+ QMessageBox.information(
+ self,
+ "Files Cleared",
+ f"Successfully deleted {deleted_count} file(s) from the translated directory."
+ )
+ except Exception as e:
+ QMessageBox.warning(self, "Error", f"Failed to clear files:\n{str(e)}")
+
+ def open_files_folder(self):
+ """Open the files folder in the system file manager."""
+ try:
+ import subprocess
+ import platform
+
+ if platform.system() == 'Windows':
+ subprocess.run(['explorer', str(self.files_dir)])
+ elif platform.system() == 'Darwin': # macOS
+ subprocess.run(['open', str(self.files_dir)])
+ else: # Linux
+ subprocess.run(['xdg-open', str(self.files_dir)])
+
+ except Exception as e:
+ QMessageBox.warning(self, "Error", f"Failed to open folder:\n{str(e)}")
+
+ def open_translated_folder(self):
+ """Open the translated folder in the system file manager."""
+ try:
+ import subprocess
+ import platform
+
+ if platform.system() == 'Windows':
+ subprocess.run(['explorer', str(self.translated_dir)])
+ elif platform.system() == 'Darwin': # macOS
+ subprocess.run(['open', str(self.translated_dir)])
+ else: # Linux
+ subprocess.run(['xdg-open', str(self.translated_dir)])
+
+ except Exception as e:
+ QMessageBox.warning(self, "Error", f"Failed to open folder:\n{str(e)}")
+
+ def get_input_files(self):
+ """Get list of input files for processing."""
+ input_files = []
+ for i in range(self.input_tree.topLevelItemCount()):
+ item = self.input_tree.topLevelItem(i)
+ file_path = item.data(0, Qt.UserRole)
+ input_files.append(Path(file_path))
+ return input_files
+
+ def closeEvent(self, event):
+ """Handle widget close event."""
+ if hasattr(self, 'refresh_timer'):
+ self.refresh_timer.stop()
+ event.accept()
diff --git a/gui/main.py b/gui/main.py
index 579f2fc..1ca116f 100644
--- a/gui/main.py
+++ b/gui/main.py
@@ -22,6 +22,8 @@ from gui.rpgmaker_tab import RPGMakerTab
from gui.other_modules_tab import OtherModulesTab
from gui.log_viewer import LogViewer
from gui.file_manager import FileManager
+from gui.file_manager_tab import FileManagerTab
+from gui.translation_tab import TranslationTab
class DazedMTLGUI(QMainWindow):
"""Main GUI window for the DazedMTLTool."""
@@ -118,6 +120,14 @@ class DazedMTLGUI(QMainWindow):
self.config_tab.config_changed.connect(self.on_config_changed)
self.tab_widget.addTab(self.config_tab, "Configuration")
+ # File Manager Tab (New)
+ self.file_manager_tab = FileManagerTab()
+ self.tab_widget.addTab(self.file_manager_tab, "File Manager")
+
+ # Translation Execution Tab
+ self.translation_tab = TranslationTab(self)
+ self.tab_widget.addTab(self.translation_tab, "Translation")
+
# RPG Maker MV/MZ Tab
self.rpgmaker_tab = RPGMakerTab()
self.tab_widget.addTab(self.rpgmaker_tab, "RPG Maker MV/MZ")
@@ -126,10 +136,6 @@ class DazedMTLGUI(QMainWindow):
self.other_modules_tab = OtherModulesTab()
self.tab_widget.addTab(self.other_modules_tab, "Other Modules")
- # File Manager Tab
- self.file_manager = FileManager()
- self.tab_widget.addTab(self.file_manager, "File Manager")
-
def on_config_changed(self):
"""Handle configuration changes."""
# This will be called when font scale or other settings change
@@ -330,6 +336,14 @@ class DazedMTLGUI(QMainWindow):
def set_progress(self, value):
"""Set the progress bar value (0-100)."""
self.progress_bar.setValue(value)
+
+ def get_input_files(self):
+ """Get list of input files from the file manager."""
+ try:
+ return self.file_manager_tab.get_input_files()
+ except Exception as e:
+ print(f"Warning: Could not get input files: {e}")
+ return []
def main():
diff --git a/gui/rpgmaker_tab.py b/gui/rpgmaker_tab.py
index 4d8c8a1..f2b80bf 100644
--- a/gui/rpgmaker_tab.py
+++ b/gui/rpgmaker_tab.py
@@ -21,12 +21,131 @@ except ImportError:
class RPGMakerTab(QWidget):
"""RPG Maker MV/MZ configuration tab."""
+ # Default configuration values for RPG Maker MV/MZ
+ DEFAULT_CONFIG = {
+ # General settings
+ "FIRSTLINESPEAKERS": False,
+ "FACENAME101": False,
+ "NAMES": False,
+ "BRFLAG": False,
+ "FIXTEXTWRAP": True,
+ "IGNORETLTEXT": False,
+
+ # Main dialogue codes (enabled by default)
+ "CODE401": True, # Show Text
+ "CODE405": True, # Show Text (line 4+)
+ "CODE102": True, # Show Choices
+
+ # Optional codes (disabled by default)
+ "CODE101": False, # Show Text (face)
+ "CODE408": False, # Show Text (line continuation)
+
+ # Variable codes (disabled by default)
+ "CODE122": False, # Control Variables
+
+ # Other codes (all disabled by default)
+ "CODE103": False, # Input Number
+ "CODE104": False, # Select Item
+ "CODE111": False, # Conditional Branch
+ "CODE117": False, # Common Event
+ "CODE119": False, # Set Movement Route
+ "CODE121": False, # Control Switches
+ "CODE125": False, # Change Gold
+ "CODE126": False, # Change Items
+ "CODE127": False, # Change Weapons
+ "CODE128": False, # Change Armors
+ "CODE129": False, # Change Party Member
+ "CODE132": False, # Change Battle BGM
+ "CODE133": False, # Change Victory ME
+ "CODE134": False, # Change Save Access
+ "CODE135": False, # Change Menu Access
+ "CODE136": False, # Change Encounter
+ "CODE137": False, # Change Formation Access
+ "CODE138": False, # Change Window Color
+ "CODE139": False, # Change Defeat ME
+ "CODE140": False, # Change Vehicle BGM
+ "CODE201": False, # Transfer Player
+ "CODE202": False, # Set Vehicle Location
+ "CODE203": False, # Set Event Location
+ "CODE204": False, # Scroll Map
+ "CODE205": False, # Set Move Route
+ "CODE206": False, # Get On/Off Vehicle
+ "CODE211": False, # Change Transparency
+ "CODE212": False, # Show Animation
+ "CODE213": False, # Show Balloon Icon
+ "CODE214": False, # Erase Event
+ "CODE216": False, # Change Player Followers
+ "CODE217": False, # Gather Followers
+ "CODE221": False, # Fadeout Screen
+ "CODE222": False, # Fadein Screen
+ "CODE223": False, # Tint Screen
+ "CODE224": False, # Flash Screen
+ "CODE225": False, # Shake Screen
+ "CODE230": False, # Wait
+ "CODE231": False, # Show Picture
+ "CODE232": False, # Move Picture
+ "CODE233": False, # Rotate Picture
+ "CODE234": False, # Tint Picture
+ "CODE235": False, # Erase Picture
+ "CODE236": False, # Set Weather Effect
+ "CODE241": False, # Play BGM
+ "CODE242": False, # Fadeout BGM
+ "CODE243": False, # Save BGM
+ "CODE244": False, # Resume BGM
+ "CODE245": False, # Play BGS
+ "CODE246": False, # Fadeout BGS
+ "CODE249": False, # Play ME
+ "CODE250": False, # Play SE
+ "CODE251": False, # Stop SE
+ "CODE261": False, # Show Movie
+ "CODE281": False, # Change Map Name Display
+ "CODE282": False, # Change Tileset
+ "CODE283": False, # Change Battle Background
+ "CODE284": False, # Change Parallax
+ "CODE285": False, # Get Location Info
+ "CODE301": False, # Battle Processing
+ "CODE302": False, # Shop Processing
+ "CODE303": False, # Name Input Processing
+ "CODE311": False, # Change HP
+ "CODE312": False, # Change MP
+ "CODE313": False, # Change State
+ "CODE314": False, # Recover All
+ "CODE315": False, # Change EXP
+ "CODE316": False, # Change Level
+ "CODE317": False, # Change Parameters
+ "CODE318": False, # Change Skills
+ "CODE319": False, # Change Equipment
+ "CODE320": False, # Change Name
+ "CODE321": False, # Change Class
+ "CODE322": False, # Change Actor Graphic
+ "CODE323": False, # Change Vehicle Graphic
+ "CODE324": False, # Change Nickname
+ "CODE325": False, # Change Profile
+ "CODE326": False, # Change TP
+ "CODE331": False, # Change Enemy HP
+ "CODE332": False, # Change Enemy MP
+ "CODE333": False, # Change Enemy State
+ "CODE334": False, # Enemy Recover All
+ "CODE335": False, # Enemy Appear
+ "CODE336": False, # Enemy Transform
+ "CODE337": False, # Show Battle Animation
+ "CODE339": False, # Force Action
+ "CODE340": False, # Abort Battle
+ "CODE351": False, # Open Menu Screen
+ "CODE352": False, # Open Save Screen
+ "CODE353": False, # Game Over
+ "CODE354": False, # Return to Title Screen
+ "CODE355": False, # Script
+ "CODE356": False, # Plugin Command
+ }
+
config_changed = pyqtSignal()
def __init__(self):
super().__init__()
self.config_integration = ConfigIntegration() if ConfigIntegration else None
self.init_ui()
+ self.connect_auto_apply() # Connect auto-apply before setting defaults
self.reset_to_defaults()
def init_ui(self):
@@ -75,18 +194,13 @@ class RPGMakerTab(QWidget):
button_layout = QHBoxLayout()
self.reset_button = QPushButton("Reset to Defaults")
- self.reset_button.clicked.connect(self.reset_to_defaults)
+ self.reset_button.clicked.connect(self.reset_to_defaults_with_message)
self.validate_button = QPushButton("Validate Settings")
self.validate_button.clicked.connect(self.validate_and_show_report)
- self.apply_button = QPushButton("Apply to Module")
- self.apply_button.clicked.connect(self.apply_to_module)
- self.apply_button.setToolTip("Apply current settings to the rpgmakermvmz.py module file")
-
button_layout.addWidget(self.reset_button)
button_layout.addWidget(self.validate_button)
- button_layout.addWidget(self.apply_button)
button_layout.addStretch()
scroll_layout.addLayout(button_layout)
@@ -263,29 +377,105 @@ class RPGMakerTab(QWidget):
def reset_to_defaults(self):
"""Reset all settings to default values."""
- # General config defaults
- self.first_line_speakers_cb.setChecked(False)
- self.facename_101_cb.setChecked(False)
- self.names_cb.setChecked(False)
- self.br_flag_cb.setChecked(False)
- self.fix_textwrap_cb.setChecked(True)
- self.ignore_tl_text_cb.setChecked(False)
+ # Temporarily disconnect signals to avoid multiple apply calls
+ self.disconnect_auto_apply()
- # Main dialogue codes (enabled by default)
- self.code_401_cb.setChecked(True)
- self.code_405_cb.setChecked(True)
- self.code_102_cb.setChecked(True)
+ # Apply default values from the DEFAULT_CONFIG constant
+ self.first_line_speakers_cb.setChecked(self.DEFAULT_CONFIG["FIRSTLINESPEAKERS"])
+ self.facename_101_cb.setChecked(self.DEFAULT_CONFIG["FACENAME101"])
+ self.names_cb.setChecked(self.DEFAULT_CONFIG["NAMES"])
+ self.br_flag_cb.setChecked(self.DEFAULT_CONFIG["BRFLAG"])
+ self.fix_textwrap_cb.setChecked(self.DEFAULT_CONFIG["FIXTEXTWRAP"])
+ self.ignore_tl_text_cb.setChecked(self.DEFAULT_CONFIG["IGNORETLTEXT"])
- # Optional codes (disabled by default)
- self.code_101_cb.setChecked(False)
- self.code_408_cb.setChecked(False)
+ # Main dialogue codes
+ self.code_401_cb.setChecked(self.DEFAULT_CONFIG["CODE401"])
+ self.code_405_cb.setChecked(self.DEFAULT_CONFIG["CODE405"])
+ self.code_102_cb.setChecked(self.DEFAULT_CONFIG["CODE102"])
- # Variable codes (disabled by default)
- self.code_122_cb.setChecked(False)
+ # Optional codes
+ self.code_101_cb.setChecked(self.DEFAULT_CONFIG["CODE101"])
+ self.code_408_cb.setChecked(self.DEFAULT_CONFIG["CODE408"])
- # Other codes (all disabled by default)
+ # Variable codes
+ self.code_122_cb.setChecked(self.DEFAULT_CONFIG["CODE122"])
+
+ # Other codes - apply defaults from the constant
+ for code, cb in self.other_code_checkboxes.items():
+ default_key = f"CODE{code}"
+ if default_key in self.DEFAULT_CONFIG:
+ cb.setChecked(self.DEFAULT_CONFIG[default_key])
+ else:
+ cb.setChecked(False) # Fallback to False if not in defaults
+
+ # Reconnect signals and apply changes once
+ self.connect_auto_apply()
+ self.apply_to_module(show_messages=False)
+
+ def reset_to_defaults_with_message(self):
+ """Reset to defaults and show confirmation message (for button clicks)."""
+ self.reset_to_defaults()
+ QMessageBox.information(
+ self,
+ "Reset Complete",
+ "All settings have been reset to their default values and applied to the module."
+ )
+
+ def disconnect_auto_apply(self):
+ """Disconnect all checkboxes from auto-apply to prevent multiple calls."""
+ try:
+ # General settings checkboxes
+ self.first_line_speakers_cb.stateChanged.disconnect()
+ self.facename_101_cb.stateChanged.disconnect()
+ self.names_cb.stateChanged.disconnect()
+ self.br_flag_cb.stateChanged.disconnect()
+ self.fix_textwrap_cb.stateChanged.disconnect()
+ self.ignore_tl_text_cb.stateChanged.disconnect()
+
+ # Main dialogue codes
+ self.code_401_cb.stateChanged.disconnect()
+ self.code_405_cb.stateChanged.disconnect()
+ self.code_102_cb.stateChanged.disconnect()
+
+ # Optional codes
+ self.code_101_cb.stateChanged.disconnect()
+ self.code_408_cb.stateChanged.disconnect()
+
+ # Variable codes
+ self.code_122_cb.stateChanged.disconnect()
+
+ # Other codes
+ for cb in self.other_code_checkboxes.values():
+ cb.stateChanged.disconnect()
+ except TypeError:
+ # Ignore if signals are not connected
+ pass
+
+ def connect_auto_apply(self):
+ """Connect all checkboxes to auto-apply changes when modified."""
+ # General settings checkboxes
+ self.first_line_speakers_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False))
+ self.facename_101_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False))
+ self.names_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False))
+ self.br_flag_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False))
+ self.fix_textwrap_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False))
+ self.ignore_tl_text_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False))
+
+ # Main dialogue codes
+ self.code_401_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False))
+ self.code_405_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False))
+ self.code_102_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False))
+
+ # Optional codes
+ self.code_101_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False))
+ self.code_408_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False))
+
+ # Variable codes
+ self.code_122_cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False))
+
+ # Other codes
for cb in self.other_code_checkboxes.values():
- cb.setChecked(False)
+ cb.stateChanged.connect(lambda: self.apply_to_module(show_messages=False))
def get_config(self):
"""Get current configuration as dictionary."""
@@ -427,35 +617,60 @@ class RPGMakerTab(QWidget):
return is_valid
- def apply_to_module(self):
+ def apply_to_module(self, show_messages=False):
"""Apply current configuration to the rpgmakermvmz.py module."""
- if not self.config_integration:
- QMessageBox.warning(self, "Warning", "Configuration integration not available")
- return
-
try:
config = self.get_config()
- success = self.config_integration.update_rpgmaker_config(config)
- if success:
+ # Direct file modification approach (completely silent)
+ module_path = Path(__file__).parent.parent / "modules" / "rpgmakermvmz.py"
+
+ if not module_path.exists():
+ if show_messages:
+ QMessageBox.critical(
+ self,
+ "Error",
+ "modules/rpgmakermvmz.py not found!\n\n"
+ "Make sure you're running the GUI from the correct directory."
+ )
+ return
+
+ # Read the current file
+ with open(module_path, 'r', encoding='utf-8') as f:
+ content = f.read()
+
+ # Update each configuration value
+ for key, value in config.items():
+ # Convert boolean to Python boolean string
+ value_str = str(value)
+
+ # Find and replace the line with this configuration
+ import re
+ pattern = rf'^{key}\s*=\s*.*$'
+ replacement = f'{key} = {value_str}'
+
+ content = re.sub(pattern, replacement, content, flags=re.MULTILINE)
+
+ # Write the updated content back
+ with open(module_path, 'w', encoding='utf-8') as f:
+ f.write(content)
+
+ # Emit signal for other components (silent)
+ self.config_changed.emit()
+
+ if show_messages:
QMessageBox.information(
self,
"Success",
"Configuration has been applied to modules/rpgmakermvmz.py\n\n"
"The module will now use these settings when running translations."
)
- else:
- QMessageBox.warning(self, "Warning", "Failed to apply configuration to module")
- except FileNotFoundError:
- QMessageBox.critical(
- self,
- "Error",
- "modules/rpgmakermvmz.py not found!\n\n"
- "Make sure you're running the GUI from the correct directory."
- )
except Exception as e:
- QMessageBox.critical(self, "Error", f"Failed to apply configuration:\n{str(e)}")
+ if show_messages:
+ QMessageBox.critical(self, "Error", f"Failed to apply configuration:\n{str(e)}")
+ # Silent failure for auto-apply - just emit signal anyway
+ self.config_changed.emit()
def load_from_module(self):
"""Load configuration from the actual module file."""
diff --git a/gui/translation_tab.py b/gui/translation_tab.py
new file mode 100644
index 0000000..22c3641
--- /dev/null
+++ b/gui/translation_tab.py
@@ -0,0 +1,489 @@
+#!/usr/bin/env python3
+"""
+Translation Execution Tab for DazedMTLTool GUI
+
+Handles running the translation process with configured settings.
+Each module has its own default settings in its folder without backups.
+"""
+
+import os
+import json
+from pathlib import Path
+from PyQt5.QtWidgets import (
+ QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QGroupBox,
+ QComboBox, QTextEdit, QProgressBar, QMessageBox, QListWidget,
+ QListWidgetItem, QSplitter, QCheckBox
+)
+from PyQt5.QtCore import Qt, QThread, pyqtSignal, QTimer
+from PyQt5.QtGui import QFont, QColor
+
+
+class TranslationWorker(QThread):
+ """Worker thread for translation process."""
+
+ progress_updated = pyqtSignal(int)
+ status_updated = pyqtSignal(str)
+ log_updated = pyqtSignal(str)
+ finished = pyqtSignal(bool) # True if successful
+
+ def __init__(self, config, files_to_translate):
+ super().__init__()
+ self.config = config
+ self.files_to_translate = files_to_translate
+ self.is_cancelled = False
+
+ def run(self):
+ """Run the translation process."""
+ try:
+ total_files = len(self.files_to_translate)
+ if total_files == 0:
+ self.status_updated.emit("No files to translate")
+ self.finished.emit(False)
+ return
+
+ self.status_updated.emit(f"Starting translation of {total_files} files...")
+
+ for i, file_path in enumerate(self.files_to_translate):
+ if self.is_cancelled:
+ self.status_updated.emit("Translation cancelled")
+ self.finished.emit(False)
+ return
+
+ progress = int((i / total_files) * 100)
+ self.progress_updated.emit(progress)
+ self.status_updated.emit(f"Translating: {file_path.name}")
+ self.log_updated.emit(f"Processing file: {file_path}")
+
+ # Here we would call the actual translation module
+ success = self.translate_file(file_path)
+
+ if not success:
+ self.log_updated.emit(f"Failed to translate: {file_path.name}")
+ self.status_updated.emit(f"Error translating {file_path.name}")
+ self.finished.emit(False)
+ return
+ else:
+ self.log_updated.emit(f"Successfully translated: {file_path.name}")
+
+ self.progress_updated.emit(100)
+ self.status_updated.emit(f"Translation completed successfully!")
+ self.finished.emit(True)
+
+ except Exception as e:
+ self.log_updated.emit(f"Translation error: {str(e)}")
+ self.status_updated.emit(f"Translation failed: {str(e)}")
+ self.finished.emit(False)
+
+ def translate_file(self, file_path):
+ """Translate a single file using the appropriate module."""
+ try:
+ import sys
+ from pathlib import Path
+
+ # Add project root to path
+ project_root = Path(__file__).parent.parent
+ sys.path.insert(0, str(project_root))
+
+ # Import the main translation module
+ from modules.main import translate_file
+
+ # Create module-specific config for this file
+ self.create_module_config(file_path)
+
+ # Call the translation function
+ result = translate_file(str(file_path))
+ return result
+
+ except Exception as e:
+ self.log_updated.emit(f"Error in translate_file: {str(e)}")
+ return False
+
+ def create_module_config(self, file_path):
+ """Create module-specific configuration without backups."""
+ try:
+ # Determine the module based on file extension and content
+ module_name = self.determine_module(file_path)
+
+ if module_name:
+ # Create module directory if it doesn't exist
+ module_dir = Path(__file__).parent.parent / "modules" / module_name
+ module_dir.mkdir(exist_ok=True)
+
+ # Create default settings file
+ settings_file = module_dir / "settings.json"
+
+ # Only create if it doesn't exist (no overwriting)
+ if not settings_file.exists():
+ default_settings = self.get_default_settings(module_name)
+ with open(settings_file, 'w', encoding='utf-8') as f:
+ json.dump(default_settings, f, indent=2, ensure_ascii=False)
+
+ self.log_updated.emit(f"Created default settings for {module_name}")
+
+ except Exception as e:
+ self.log_updated.emit(f"Error creating module config: {str(e)}")
+
+ def determine_module(self, file_path):
+ """Determine which module to use based on file type."""
+ suffix = file_path.suffix.lower()
+
+ if suffix == '.json':
+ # Check if it's RPG Maker format
+ try:
+ with open(file_path, 'r', encoding='utf-8') as f:
+ content = f.read()
+ if '"code":' in content or '"parameters":' in content:
+ return "rpgmakermvmz"
+ else:
+ return "json"
+ except:
+ return "json"
+ elif suffix == '.txt':
+ return "text"
+ elif suffix == '.csv':
+ return "csv"
+ elif suffix in ['.js', '.py']:
+ return "regex"
+ else:
+ return "text" # Default fallback
+
+ def get_default_settings(self, module_name):
+ """Get default settings for a module."""
+ base_settings = {
+ "enabled": True,
+ "backup": False, # No backups as requested
+ "output_directory": "translated",
+ "preserve_formatting": True
+ }
+
+ if module_name == "rpgmakermvmz":
+ # Use the same defaults as defined in RPG Maker tab
+ rpg_defaults = {
+ # Main dialogue codes (enabled by default)
+ "CODE401": True, # Show Text
+ "CODE405": True, # Show Text (line 4+)
+ "CODE102": True, # Show Choices
+
+ # Optional codes (disabled by default)
+ "CODE101": False, # Show Text (face)
+ "CODE408": False, # Show Text (line continuation)
+
+ # Variable codes (disabled by default)
+ "CODE122": False, # Control Variables
+
+ # General settings
+ "FIRSTLINESPEAKERS": False,
+ "FACENAME101": False,
+ "NAMES": False,
+ "BRFLAG": False,
+ "FIXTEXTWRAP": True,
+ "IGNORETLTEXT": False,
+
+ # All other codes disabled by default
+ "translate_dialogue": True,
+ "translate_choices": True,
+ "translate_items": False,
+ "translate_skills": False,
+ "translate_states": False,
+ "translate_actors": False,
+ "translate_classes": False,
+ "translate_weapons": False,
+ "translate_armors": False,
+ "translate_enemies": False,
+ "event_codes": ["401", "405", "102"] # Only main dialogue codes enabled
+ }
+ base_settings.update(rpg_defaults)
+ elif module_name == "json":
+ base_settings.update({
+ "translate_strings": True,
+ "skip_keys": ["id", "index", "type"],
+ "nested_translation": True
+ })
+ elif module_name == "text":
+ base_settings.update({
+ "line_by_line": True,
+ "skip_empty_lines": True,
+ "preserve_whitespace": True
+ })
+ elif module_name == "csv":
+ base_settings.update({
+ "has_header": True,
+ "delimiter": ",",
+ "columns_to_translate": [] # Empty means translate all text columns
+ })
+
+ return base_settings
+
+ def cancel(self):
+ """Cancel the translation process."""
+ self.is_cancelled = True
+
+
+class TranslationTab(QWidget):
+ """Translation execution tab."""
+
+ def __init__(self, parent=None):
+ super().__init__(parent)
+ self.parent_window = parent
+ self.worker = None
+ self.setup_ui()
+
+ def setup_ui(self):
+ """Set up the user interface."""
+ layout = QVBoxLayout()
+
+ # Title
+ title_label = QLabel("Translation Execution")
+ title_label.setStyleSheet("font-size: 16px; font-weight: bold; color: #007acc;")
+ layout.addWidget(title_label)
+
+ # Description
+ desc_label = QLabel(
+ "Execute the translation process on selected files. "
+ "Each module will use its default settings stored in its own folder."
+ )
+ desc_label.setWordWrap(True)
+ desc_label.setStyleSheet("color: #cccccc; margin-bottom: 10px;")
+ layout.addWidget(desc_label)
+
+ # Main content
+ content_splitter = QSplitter(Qt.Vertical)
+
+ # File selection and controls
+ controls_group = self.create_controls_section()
+ content_splitter.addWidget(controls_group)
+
+ # Progress and log section
+ progress_group = self.create_progress_section()
+ content_splitter.addWidget(progress_group)
+
+ content_splitter.setSizes([300, 200])
+ layout.addWidget(content_splitter)
+
+ # Status bar
+ self.status_label = QLabel("Ready to translate")
+ self.status_label.setStyleSheet("color: #cccccc; padding: 5px;")
+ layout.addWidget(self.status_label)
+
+ self.setLayout(layout)
+
+ def create_controls_section(self):
+ """Create the controls section."""
+ group = QGroupBox("Translation Controls")
+ layout = QVBoxLayout()
+
+ # File selection
+ file_layout = QHBoxLayout()
+
+ file_label = QLabel("Files to translate:")
+ file_label.setStyleSheet("color: #ffffff;")
+ file_layout.addWidget(file_label)
+
+ self.refresh_files_btn = QPushButton("Refresh File List")
+ self.refresh_files_btn.clicked.connect(self.refresh_file_list)
+ file_layout.addWidget(self.refresh_files_btn)
+
+ file_layout.addStretch()
+ layout.addLayout(file_layout)
+
+ # File list
+ self.file_list = QListWidget()
+ self.file_list.setSelectionMode(QListWidget.ExtendedSelection)
+ layout.addWidget(self.file_list)
+
+ # Options
+ options_layout = QHBoxLayout()
+
+ self.select_all_cb = QCheckBox("Select All Files")
+ self.select_all_cb.stateChanged.connect(self.toggle_select_all)
+ options_layout.addWidget(self.select_all_cb)
+
+ self.dry_run_cb = QCheckBox("Dry Run (test without translating)")
+ options_layout.addWidget(self.dry_run_cb)
+
+ options_layout.addStretch()
+ layout.addLayout(options_layout)
+
+ # Action buttons
+ button_layout = QHBoxLayout()
+
+ self.start_btn = QPushButton("Start Translation")
+ self.start_btn.clicked.connect(self.start_translation)
+ self.start_btn.setStyleSheet("QPushButton { background-color: #0a7a0a; }")
+ button_layout.addWidget(self.start_btn)
+
+ self.stop_btn = QPushButton("Stop Translation")
+ self.stop_btn.clicked.connect(self.stop_translation)
+ self.stop_btn.setEnabled(False)
+ self.stop_btn.setStyleSheet("QPushButton { background-color: #aa0000; }")
+ button_layout.addWidget(self.stop_btn)
+
+ button_layout.addStretch()
+ layout.addLayout(button_layout)
+
+ group.setLayout(layout)
+ return group
+
+ def create_progress_section(self):
+ """Create the progress and log section."""
+ group = QGroupBox("Translation Progress")
+ layout = QVBoxLayout()
+
+ # Progress bar
+ self.progress_bar = QProgressBar()
+ self.progress_bar.setRange(0, 100)
+ self.progress_bar.setValue(0)
+ layout.addWidget(self.progress_bar)
+
+ # Log output
+ log_label = QLabel("Translation Log:")
+ log_label.setStyleSheet("color: #ffffff; margin-top: 10px;")
+ layout.addWidget(log_label)
+
+ self.log_output = QTextEdit()
+ self.log_output.setReadOnly(True)
+ self.log_output.setMaximumHeight(150)
+ layout.addWidget(self.log_output)
+
+ # Clear log button
+ clear_layout = QHBoxLayout()
+ clear_layout.addStretch()
+
+ self.clear_log_btn = QPushButton("Clear Log")
+ self.clear_log_btn.clicked.connect(self.clear_log)
+ clear_layout.addWidget(self.clear_log_btn)
+
+ layout.addLayout(clear_layout)
+
+ group.setLayout(layout)
+ return group
+
+ def refresh_file_list(self):
+ """Refresh the list of available files."""
+ self.file_list.clear()
+
+ try:
+ # Get files from the file manager tab
+ if hasattr(self.parent_window, 'file_manager_tab'):
+ input_files = self.parent_window.file_manager_tab.get_input_files()
+
+ for file_path in input_files:
+ item = QListWidgetItem(file_path.name)
+ item.setData(Qt.UserRole, str(file_path))
+ item.setCheckState(Qt.Checked) # Default to checked
+ self.file_list.addItem(item)
+
+ self.status_label.setText(f"Found {len(input_files)} files ready for translation")
+ else:
+ self.status_label.setText("File manager not available")
+
+ except Exception as e:
+ self.status_label.setText(f"Error loading files: {str(e)}")
+
+ def toggle_select_all(self, state):
+ """Toggle selection of all files."""
+ check_state = Qt.Checked if state == Qt.Checked else Qt.Unchecked
+
+ for i in range(self.file_list.count()):
+ item = self.file_list.item(i)
+ item.setCheckState(check_state)
+
+ def get_selected_files(self):
+ """Get list of selected files."""
+ selected_files = []
+
+ for i in range(self.file_list.count()):
+ item = self.file_list.item(i)
+ if item.checkState() == Qt.Checked:
+ file_path = Path(item.data(Qt.UserRole))
+ selected_files.append(file_path)
+
+ return selected_files
+
+ def start_translation(self):
+ """Start the translation process."""
+ selected_files = self.get_selected_files()
+
+ if not selected_files:
+ QMessageBox.warning(self, "No Files Selected", "Please select at least one file to translate.")
+ return
+
+ # Get configuration from other tabs
+ try:
+ config = {}
+ if hasattr(self.parent_window, 'config_tab'):
+ config.update(self.parent_window.config_tab.get_config())
+ if hasattr(self.parent_window, 'rpgmaker_tab'):
+ config.update(self.parent_window.rpgmaker_tab.get_config())
+
+ except Exception as e:
+ QMessageBox.critical(self, "Configuration Error", f"Failed to get configuration:\n{str(e)}")
+ return
+
+ # Confirm start
+ if not self.dry_run_cb.isChecked():
+ reply = QMessageBox.question(
+ self,
+ "Confirm Translation",
+ f"Start translation of {len(selected_files)} files?\n\nThis will create translated files in the 'translated' directory.",
+ QMessageBox.Yes | QMessageBox.No
+ )
+ if reply != QMessageBox.Yes:
+ return
+
+ # Start worker thread
+ self.worker = TranslationWorker(config, selected_files)
+ self.worker.progress_updated.connect(self.progress_bar.setValue)
+ self.worker.status_updated.connect(self.status_label.setText)
+ self.worker.log_updated.connect(self.add_log_message)
+ self.worker.finished.connect(self.on_translation_finished)
+
+ # Update UI state
+ self.start_btn.setEnabled(False)
+ self.stop_btn.setEnabled(True)
+ self.progress_bar.setValue(0)
+
+ # Start translation
+ self.worker.start()
+ self.add_log_message(f"Starting translation of {len(selected_files)} files...")
+
+ def stop_translation(self):
+ """Stop the translation process."""
+ if self.worker and self.worker.isRunning():
+ self.worker.cancel()
+ self.add_log_message("Cancelling translation...")
+ self.status_label.setText("Stopping translation...")
+
+ def on_translation_finished(self, success):
+ """Handle translation completion."""
+ # Update UI state
+ self.start_btn.setEnabled(True)
+ self.stop_btn.setEnabled(False)
+
+ if success:
+ self.add_log_message("Translation completed successfully!")
+ QMessageBox.information(self, "Translation Complete", "All files have been translated successfully!")
+
+ # Refresh file manager if available
+ if hasattr(self.parent_window, 'file_manager_tab'):
+ self.parent_window.file_manager_tab.refresh_file_lists()
+ else:
+ self.add_log_message("Translation failed or was cancelled.")
+
+ self.worker = None
+
+ def add_log_message(self, message):
+ """Add a message to the log output."""
+ import datetime
+ timestamp = datetime.datetime.now().strftime("%H:%M:%S")
+ formatted_message = f"[{timestamp}] {message}"
+ self.log_output.append(formatted_message)
+
+ # Auto-scroll to bottom
+ scrollbar = self.log_output.verticalScrollBar()
+ scrollbar.setValue(scrollbar.maximum())
+
+ def clear_log(self):
+ """Clear the log output."""
+ self.log_output.clear()
+ self.add_log_message("Log cleared")
diff --git a/modules/rpgmakermvmz.py b/modules/rpgmakermvmz.py
index d6d2b65..9875345 100644
--- a/modules/rpgmakermvmz.py
+++ b/modules/rpgmakermvmz.py
@@ -87,12 +87,12 @@ TRANSLATION_CONFIG = TranslationConfig(
LEAVE = False
# Config (Default)
-FIRSTLINESPEAKERS = False # If 1st line of 401 is a speaker, set to True (False)
-FACENAME101 = False # Find Speakers in 101 Codes based on Face Name (False)
-NAMES = False # Output a list of all the character names found (False)
-BRFLAG = False # If the game uses
instead (False)
-FIXTEXTWRAP = True # Overwrites textwrap (True)
-IGNORETLTEXT = False # Ignores all translated text. (False)
+FIRSTLINESPEAKERS = False
+FACENAME101 = False
+NAMES = False
+BRFLAG = False
+FIXTEXTWRAP = True
+IGNORETLTEXT = False
# Dialogue / Scroll / Choices (Main Codes)
CODE401 = True
@@ -100,8 +100,8 @@ CODE405 = True
CODE102 = True
# Optional
-CODE101 = False # Turn this one on when names exist in 101
-CODE408 = False # Warning, translates comments and can inflate costs.
+CODE101 = False
+CODE408 = False
# Variables
CODE122 = False
diff --git a/set_defaults.py b/set_defaults.py
index 9ed9616..dfa67ce 100644
--- a/set_defaults.py
+++ b/set_defaults.py
@@ -6,19 +6,19 @@ def set_defaults(file_path):
# Define the default values with comments
defaults = {
- 'FIRSTLINESPEAKERS': 'False # If 1st line of 401 is a speaker, set to True (False)',
- 'FACENAME101': 'False # Find Speakers in 101 Codes based on Face Name (False)',
- 'NAMES': 'False # Output a list of all the character names found (False)',
- 'BRFLAG': 'False # If the game uses
instead (False)',
- 'FIXTEXTWRAP': 'True # Overwrites textwrap (True)',
- 'IGNORETLTEXT': 'False # Ignores all translated text. (False)',
+ 'FIRSTLINESPEAKERS': 'False',
+ 'FACENAME101': 'False',
+ 'NAMES': 'False',
+ 'BRFLAG': 'False',
+ 'FIXTEXTWRAP': 'True',
+ 'IGNORETLTEXT': 'False',
# Dialogue / Scroll / Choices (Main Codes)
'CODE401': 'True',
'CODE405': 'True',
'CODE102': 'True',
# Optional
- 'CODE101': 'False # Turn this one on when names exist in 101',
- 'CODE408': 'False # Warning, translates comments and can inflate costs.',
+ 'CODE101': 'False',
+ 'CODE408': 'False',
# Variables
'CODE122': 'False',
# Other