197 lines
8.7 KiB
Python
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
|