import os import tkinter as tk from typing import Optional, TYPE_CHECKING from .cfd_app_config import LocaleStrings if TYPE_CHECKING: from custom_file_dialog import CustomFileDialog class NavigationManager: """Manages directory navigation, history, and path handling.""" def __init__(self, dialog: 'CustomFileDialog') -> None: """ Initializes the NavigationManager. Args: dialog: The main CustomFileDialog instance. """ self.dialog = dialog def handle_path_entry_return(self, event: tk.Event) -> None: """ Handles the Return key press in the path entry field. It attempts to navigate to the entered path. If the path is a file, it navigates to the containing directory and selects the file. Args: event: The tkinter event that triggered this handler. """ path_text = self.dialog.widget_manager.path_entry.get().strip() potential_path = os.path.realpath(os.path.expanduser(path_text)) if os.path.isdir(potential_path): self.navigate_to(potential_path) elif os.path.isfile(potential_path): directory = os.path.dirname(potential_path) filename = os.path.basename(potential_path) self.navigate_to(directory, file_to_select=filename) else: self.dialog.widget_manager.search_status_label.config( text=f"{LocaleStrings.CFD['path_not_found']}: {self.dialog.shorten_text(path_text, 50)}") def navigate_to(self, path: str, file_to_select: Optional[str] = None) -> None: """ Navigates to a specified directory path. This is the core navigation method. It validates the path, checks for read permissions, updates the dialog's current directory, manages the navigation history, and refreshes the file view. Args: path (str): The absolute path to navigate to. file_to_select (str, optional): If provided, this filename will be selected after navigation. Defaults to None. """ try: real_path = os.path.realpath( os.path.abspath(os.path.expanduser(path))) if not os.path.isdir(real_path): self.dialog.widget_manager.search_status_label.config( text=f"{LocaleStrings.CFD['error_title']}: {LocaleStrings.CFD['directory']} '{os.path.basename(path)}' {LocaleStrings.CFD['not_found']}") return if not os.access(real_path, os.R_OK): self.dialog.widget_manager.search_status_label.config( text=f"{LocaleStrings.CFD['access_to']} '{os.path.basename(path)}' {LocaleStrings.CFD['denied']}") return self.dialog.current_dir = real_path if self.dialog.history_pos < len(self.dialog.history) - 1: self.dialog.history = self.dialog.history[:self.dialog.history_pos + 1] if not self.dialog.history or self.dialog.history[-1] != self.dialog.current_dir: self.dialog.history.append(self.dialog.current_dir) self.dialog.history_pos = len(self.dialog.history) - 1 self.dialog.widget_manager.search_animation.stop() self.dialog.view_manager.populate_files( item_to_select=file_to_select) self.update_nav_buttons() self.dialog.update_status_bar() self.dialog.update_action_buttons_state() except Exception as e: self.dialog.widget_manager.search_status_label.config( text=f"{LocaleStrings.CFD['error_title']}: {e}") def go_back(self) -> None: """Navigates to the previous directory in the history.""" if self.dialog.history_pos > 0: self.dialog.history_pos -= 1 self.dialog.current_dir = self.dialog.history[self.dialog.history_pos] self._update_ui_after_navigation() def go_forward(self) -> None: """Navigates to the next directory in the history.""" if self.dialog.history_pos < len(self.dialog.history) - 1: self.dialog.history_pos += 1 self.dialog.current_dir = self.dialog.history[self.dialog.history_pos] self._update_ui_after_navigation() def go_up_level(self) -> None: """Navigates to the parent directory of the current directory.""" new_path = os.path.dirname(self.dialog.current_dir) if new_path != self.dialog.current_dir: self.navigate_to(new_path) def _update_ui_after_navigation(self) -> None: """Updates all necessary UI components after a navigation action.""" self.dialog.view_manager.populate_files() self.update_nav_buttons() self.dialog.update_status_bar() self.dialog.update_action_buttons_state() def update_nav_buttons(self) -> None: """Updates the state of the back and forward navigation buttons.""" self.dialog.widget_manager.back_button.config( state=tk.NORMAL if self.dialog.history_pos > 0 else tk.DISABLED) self.dialog.widget_manager.forward_button.config(state=tk.NORMAL if self.dialog.history_pos < len( self.dialog.history) - 1 else tk.DISABLED)