diff --git a/src/main_window.py b/src/main_window.py index 3c4a0d5..0f27a59 100644 --- a/src/main_window.py +++ b/src/main_window.py @@ -386,6 +386,7 @@ class MainWindow(QMainWindow): self.file_list.folder_entered.connect(self._navigate_to) self.file_list.file_rename_requested.connect(self._rename_file) self.file_list.file_delete_requested.connect(self._delete_file) + self.file_list.files_delete_requested.connect(self._delete_files) self.file_list.file_move_requested.connect(self._move_file) self.file_list.files_dropped.connect(self._handle_files_dropped) @@ -648,16 +649,22 @@ class MainWindow(QMainWindow): dialog = DeleteDialog(paths, self) if dialog.exec(): errors = [] + deleted_paths = [] for path in paths: try: if os.path.isdir(path): shutil.rmtree(path) else: os.remove(path) + deleted_paths.append(path) except Exception as e: errors.append(f"{os.path.basename(path)}: {e}") - self._refresh() + # Gelöschte Einträge direkt aus der Liste entfernen + if deleted_paths: + self.file_list.remove_paths(deleted_paths) + self.folder_tree.refresh() + self._update_count() if errors: QMessageBox.warning( diff --git a/src/widgets/file_list.py b/src/widgets/file_list.py index 90949d7..5fbf108 100644 --- a/src/widgets/file_list.py +++ b/src/widgets/file_list.py @@ -116,25 +116,33 @@ class FileListModel(QAbstractTableModel): return try: - entries = os.scandir(path) + # Verzeichnis-Cache invalidieren + try: + fd = os.open(path, os.O_RDONLY | os.O_DIRECTORY) + os.close(fd) + except OSError: + pass + folders = [] files = [] - for entry in entries: + for name in os.listdir(path): + entry_path = os.path.join(path, name) try: - stat = entry.stat() + stat = os.stat(entry_path) + is_dir = os.path.isdir(entry_path) item = FileItem( - name=entry.name, - path=entry.path, - is_dir=entry.is_dir(), - size=stat.st_size if entry.is_file() else 0, + name=name, + path=entry_path, + is_dir=is_dir, + size=stat.st_size if not is_dir else 0, modified=datetime.fromtimestamp(stat.st_mtime) ) - if entry.is_dir(): + if is_dir: folders.append(item) else: files.append(item) - except (PermissionError, OSError): + except (PermissionError, OSError, FileNotFoundError): continue # Natürliche Sortierung @@ -159,6 +167,18 @@ class FileListModel(QAbstractTableModel): if self.current_path: self.set_path(self.current_path) + def remove_paths(self, paths: list[str]): + """Entfernt Einträge mit den angegebenen Pfaden aus dem Model.""" + paths_set = set(paths) + # Finde Indizes der zu löschenden Items (von hinten nach vorne) + indices_to_remove = [i for i, item in enumerate(self.items) if item.path in paths_set] + indices_to_remove.sort(reverse=True) + + for idx in indices_to_remove: + self.beginRemoveRows(QModelIndex(), idx, idx) + del self.items[idx] + self.endRemoveRows() + class FileListWidget(QTableView): """Dateiliste mit Kontextmenü und Drag & Drop.""" @@ -167,7 +187,8 @@ class FileListWidget(QTableView): file_double_clicked = pyqtSignal(str) # path folder_entered = pyqtSignal(str) # path file_rename_requested = pyqtSignal(str) # path - file_delete_requested = pyqtSignal(str) # path + file_delete_requested = pyqtSignal(str) # path (einzelne Datei) + files_delete_requested = pyqtSignal(object) # paths (mehrere Dateien) file_move_requested = pyqtSignal(str) # path files_dropped = pyqtSignal(list, str) # source_paths, target_folder @@ -433,6 +454,14 @@ class FileListWidget(QTableView): props_action.triggered.connect(lambda: self.properties_requested.emit([item.path])) menu.addAction(props_action) + menu.addSeparator() + + # Aktualisieren + refresh_action = QAction("🔄 Aktualisieren", self) + refresh_action.setShortcut("F5") + refresh_action.triggered.connect(self.refresh_requested.emit) + menu.addAction(refresh_action) + def _build_multi_item_context_menu(self, menu: QMenu, items: list[FileItem]): """Erstellt Kontextmenü für mehrere Elemente.""" paths = [item.path for item in items] @@ -470,6 +499,14 @@ class FileListWidget(QTableView): props_action.triggered.connect(lambda: self.properties_requested.emit(paths)) menu.addAction(props_action) + menu.addSeparator() + + # Aktualisieren + refresh_action = QAction("🔄 Aktualisieren", self) + refresh_action.setShortcut("F5") + refresh_action.triggered.connect(self.refresh_requested.emit) + menu.addAction(refresh_action) + def _open_external(self, path: str): """Öffnet eine Datei mit der Standard-Anwendung.""" try: @@ -522,8 +559,8 @@ class FileListWidget(QTableView): def _delete_items(self, items: list): """Löscht mehrere Elemente.""" - for item in items: - self.file_delete_requested.emit(item.path) + paths = [item.path for item in items] + self.files_delete_requested.emit(paths) def get_selected_items(self) -> list[FileItem]: """Gibt die ausgewählten Elemente zurück.""" @@ -542,8 +579,14 @@ class FileListWidget(QTableView): def refresh(self): """Aktualisiert die Dateiliste.""" + self.clearSelection() self.list_model.refresh() + def remove_paths(self, paths: list[str]): + """Entfernt Einträge mit den angegebenen Pfaden aus der Liste.""" + self.clearSelection() + self.list_model.remove_paths(paths) + # Drag & Drop def startDrag(self, supportedActions): """Startet einen Drag-Vorgang.""" diff --git a/src/widgets/preview_panel.py b/src/widgets/preview_panel.py index c4d8afe..0df723a 100644 --- a/src/widgets/preview_panel.py +++ b/src/widgets/preview_panel.py @@ -532,6 +532,16 @@ class PdfPreview(QWidget): self.draw_btn.clicked.connect(self._open_in_draw) toolbar_layout.addWidget(self.draw_btn) + toolbar_layout.addSpacing(8) + + # Darkmode Toggle + self.darkmode_btn = QPushButton("🌙") + self.darkmode_btn.setToolTip("Darkmode umschalten") + self.darkmode_btn.setFixedSize(32, 28) + self.darkmode_btn.setCheckable(True) + self.darkmode_btn.clicked.connect(self._toggle_darkmode) + toolbar_layout.addWidget(self.darkmode_btn) + layout.addWidget(toolbar) # PDF Document und View @@ -564,9 +574,14 @@ class PdfPreview(QWidget): self.page_combo.setCurrentIndex(i) break + # Darkmode + darkmode = self.settings.value('pdf_darkmode', False, type=bool) + self.darkmode_btn.setChecked(darkmode) + # Einstellungen anwenden self._apply_zoom_mode() self._apply_page_mode() + self._apply_darkmode(darkmode) def _save_settings(self): """Speichert PDF-Einstellungen.""" @@ -614,6 +629,27 @@ class PdfPreview(QWidget): self.pdf_view.setPageMode(mode) break + def _toggle_darkmode(self): + """Schaltet den Darkmode um.""" + is_dark = self.darkmode_btn.isChecked() + self._apply_darkmode(is_dark) + self.settings.setValue('pdf_darkmode', is_dark) + + def _apply_darkmode(self, enabled: bool): + """Wendet den Darkmode an.""" + if enabled: + self.darkmode_btn.setText("☀️") + self.darkmode_btn.setToolTip("Lightmode umschalten") + self.pdf_view.setStyleSheet(""" + QPdfView { + background-color: #2d2d2d; + } + """) + else: + self.darkmode_btn.setText("🌙") + self.darkmode_btn.setToolTip("Darkmode umschalten") + self.pdf_view.setStyleSheet("") + def load_pdf(self, path: str) -> bool: """Lädt eine PDF-Datei.""" self._current_path = path