Files
HereIAm/src/options_dialog.py
Jerico Thomas d93ce669b1 1.0.1
2025-08-01 12:29:34 -04:00

197 lines
8.7 KiB
Python

from PyQt5 import QtWidgets, QtCore, QtGui
import logging
import subprocess
import os
from logging_config import get_logger, get_log_directory_path
from launch_agent import get_launch_agent_manager
class OptionsDialog(QtWidgets.QDialog):
"""Options dialog for configuring HereIAm settings"""
def __init__(self, current_wait_time=240, current_start_enabled=True, current_log_level="INFO", current_move_px=10, current_launch_at_startup=False, parent=None):
super().__init__(parent)
self.logger = get_logger(__name__)
# Store current values
self.wait_time = current_wait_time
self.start_enabled = current_start_enabled
self.log_level = current_log_level
self.move_px = current_move_px
self.launch_at_startup = current_launch_at_startup
# Initialize launch agent manager
self.launch_agent_manager = get_launch_agent_manager()
self.setupUI()
self.load_current_values()
def setupUI(self):
"""Setup the dialog UI"""
self.setWindowTitle("HereIAm Options")
self.setModal(True)
self.setFixedSize(400, 260) # Increased height to accommodate new button
# Ensure dialog appears on top and is properly managed
self.setWindowFlags(QtCore.Qt.Dialog | QtCore.Qt.WindowStaysOnTopHint)
# Remove WA_DeleteOnClose to prevent automatic deletion conflicts
# Main layout
layout = QtWidgets.QVBoxLayout(self)
layout.setSpacing(10) # Reduce spacing between elements
# Create form layout
form_layout = QtWidgets.QFormLayout()
form_layout.setVerticalSpacing(8) # Reduce vertical spacing between form rows
form_layout.setHorizontalSpacing(10) # Set horizontal spacing
# Wait Time setting
self.wait_time_spinbox = QtWidgets.QSpinBox()
self.wait_time_spinbox.setRange(10, 3600) # 10 seconds to 1 hour
self.wait_time_spinbox.setSuffix(" seconds")
self.wait_time_spinbox.setToolTip("Time to wait before moving mouse when idle (Range: 10-3600 seconds)")
self.wait_time_spinbox.setKeyboardTracking(True) # Enable keyboard input
self.wait_time_spinbox.setButtonSymbols(QtWidgets.QAbstractSpinBox.UpDownArrows) # Ensure arrows are visible
self.wait_time_spinbox.lineEdit().setReadOnly(False) # Allow typing in the field
form_layout.addRow("Wait Time (10-3600s):", self.wait_time_spinbox)
# Mouse Movement Distance setting
self.move_px_spinbox = QtWidgets.QSpinBox()
self.move_px_spinbox.setRange(1, 100) # 1 to 100 pixels
self.move_px_spinbox.setSuffix(" pixels")
self.move_px_spinbox.setToolTip("Distance to move mouse in pixels (Range: 1-100 pixels)")
self.move_px_spinbox.setKeyboardTracking(True) # Enable keyboard input
self.move_px_spinbox.setButtonSymbols(QtWidgets.QAbstractSpinBox.UpDownArrows) # Ensure arrows are visible
self.move_px_spinbox.lineEdit().setReadOnly(False) # Allow typing in the field
form_layout.addRow("Movement Distance (1-100px):", self.move_px_spinbox)
# Start Enabled setting
self.start_enabled_checkbox = QtWidgets.QCheckBox()
self.start_enabled_checkbox.setToolTip("Start HereIAm enabled when application launches")
form_layout.addRow("Start Enabled:", self.start_enabled_checkbox)
# Launch at Startup setting
self.launch_at_startup_checkbox = QtWidgets.QCheckBox()
self.launch_at_startup_checkbox.setToolTip("Automatically launch HereIAm when you log in to macOS")
form_layout.addRow("Launch at Startup:", self.launch_at_startup_checkbox)
# Log Level setting
self.log_level_combo = QtWidgets.QComboBox()
self.log_level_combo.addItems(["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"])
self.log_level_combo.setToolTip("Set the logging level for the application")
form_layout.addRow("Log Level:", self.log_level_combo)
layout.addLayout(form_layout)
# Open Logs button
logs_button = QtWidgets.QPushButton("Open Logs Directory")
logs_button.setToolTip("Open the directory containing HereIAm log files")
logs_button.clicked.connect(self.open_logs_directory)
layout.addWidget(logs_button)
# Button box
button_box = QtWidgets.QDialogButtonBox(
QtWidgets.QDialogButtonBox.Save | QtWidgets.QDialogButtonBox.Cancel
)
button_box.accepted.connect(self.accept)
button_box.rejected.connect(self.reject)
layout.addWidget(button_box)
# Center the dialog on screen
self.center_on_screen()
def center_on_screen(self):
"""Center the dialog on the screen"""
screen = QtWidgets.QApplication.primaryScreen()
if screen:
screen_geometry = screen.geometry()
dialog_geometry = self.geometry()
x = (screen_geometry.width() - dialog_geometry.width()) // 2
y = (screen_geometry.height() - dialog_geometry.height()) // 2
self.move(x, y)
def showEvent(self, event):
"""Called when dialog is shown"""
super().showEvent(event)
self.raise_()
self.activateWindow()
def closeEvent(self, event):
"""Called when dialog is closed"""
self.logger.debug("Options dialog closing")
super().closeEvent(event)
def load_current_values(self):
"""Load current values into the form"""
self.wait_time_spinbox.setValue(self.wait_time)
self.move_px_spinbox.setValue(self.move_px)
self.start_enabled_checkbox.setChecked(self.start_enabled)
# Check current startup status from the system
current_startup_enabled = self.launch_agent_manager.is_startup_enabled()
self.launch_at_startup_checkbox.setChecked(current_startup_enabled)
# Set log level
index = self.log_level_combo.findText(self.log_level)
if index >= 0:
self.log_level_combo.setCurrentIndex(index)
def get_values(self):
"""Get the values from the form"""
try:
return {
'wait_time': self.wait_time_spinbox.value(),
'move_px': self.move_px_spinbox.value(),
'start_enabled': self.start_enabled_checkbox.isChecked(),
'launch_at_startup': self.launch_at_startup_checkbox.isChecked(),
'log_level': self.log_level_combo.currentText()
}
except AttributeError as e:
# If widgets are already destroyed, return None
self.logger.error(f"Error getting dialog values: {e}")
return None
def open_logs_directory(self):
"""Open the logs directory in Finder (macOS)"""
try:
log_dir = get_log_directory_path()
self.logger.debug(f"Opening logs directory: {log_dir}")
# Ensure the directory exists
if not os.path.exists(log_dir):
self.logger.warning(f"Logs directory does not exist: {log_dir}")
QtWidgets.QMessageBox.warning(
self,
"Directory Not Found",
f"The logs directory does not exist yet:\n{log_dir}\n\nLogs will be created when the application runs."
)
return
# Open directory in Finder on macOS
subprocess.run(['open', log_dir], check=True)
self.logger.info(f"Successfully opened logs directory: {log_dir}")
except subprocess.CalledProcessError as e:
self.logger.error(f"Failed to open logs directory: {e}")
QtWidgets.QMessageBox.critical(
self,
"Error Opening Directory",
f"Failed to open logs directory:\n{str(e)}"
)
except Exception as e:
self.logger.error(f"Unexpected error opening logs directory: {e}")
QtWidgets.QMessageBox.critical(
self,
"Error",
f"An unexpected error occurred:\n{str(e)}"
)
@staticmethod
def get_options(current_wait_time=240, current_start_enabled=True, current_log_level="INFO", current_move_px=10, current_launch_at_startup=False, parent=None):
"""Static method to show the dialog and return values"""
dialog = OptionsDialog(current_wait_time, current_start_enabled, current_log_level, current_move_px, current_launch_at_startup, parent)
if dialog.exec_() == QtWidgets.QDialog.Accepted:
return dialog.get_values(), True
return None, False