from collections import defaultdict from PyQt5.QtCore import pyqtSignal, Qt from PyQt5.QtWidgets import ( QApplication, QDialog, QHBoxLayout, QLabel, QListWidget, QListWidgetItem, QMessageBox, QPushButton, QVBoxLayout, QAbstractItemView ) from ...i18n.translator import get_translation from ..main_window import get_app_icon_object from ...utils.resolution import get_dark_theme class DownloadExtractedLinksDialog(QDialog): """ A dialog to select and initiate the download for extracted, supported links from external cloud services like Mega, Google Drive, and Dropbox. """ download_requested = pyqtSignal(list) def __init__(self, links_data, parent_app, parent=None): """ Initializes the dialog. Args: links_data (list): A list of dictionaries, each containing info about an extracted link. parent_app (DownloaderApp): A reference to the main application window for theming and translations. parent (QWidget, optional): The parent widget. Defaults to None. """ super().__init__(parent) self.links_data = links_data self.parent_app = parent_app app_icon = get_app_icon_object() if not app_icon.isNull(): self.setWindowIcon(app_icon) scale_factor = getattr(self.parent_app, 'scale_factor', 1.0) base_width, base_height = 600, 450 self.setMinimumSize(int(base_width * scale_factor), int(base_height * scale_factor)) self.resize(int(base_width * scale_factor * 1.1), int(base_height * scale_factor * 1.1)) self._init_ui() self._retranslate_ui() self._apply_theme() def _init_ui(self): """Initializes all UI components and layouts for the dialog.""" layout = QVBoxLayout(self) self.main_info_label = QLabel() self.main_info_label.setAlignment(Qt.AlignHCenter | Qt.AlignTop) self.main_info_label.setWordWrap(True) layout.addWidget(self.main_info_label) self.links_list_widget = QListWidget() self.links_list_widget.setSelectionMode(QAbstractItemView.NoSelection) self._populate_list() layout.addWidget(self.links_list_widget) button_layout = QHBoxLayout() self.select_all_button = QPushButton() self.select_all_button.clicked.connect(lambda: self._set_all_items_checked(Qt.Checked)) button_layout.addWidget(self.select_all_button) self.deselect_all_button = QPushButton() self.deselect_all_button.clicked.connect(lambda: self._set_all_items_checked(Qt.Unchecked)) button_layout.addWidget(self.deselect_all_button) button_layout.addStretch() self.download_button = QPushButton() self.download_button.clicked.connect(self._handle_download_selected) self.download_button.setDefault(True) button_layout.addWidget(self.download_button) self.cancel_button = QPushButton() self.cancel_button.clicked.connect(self.reject) button_layout.addWidget(self.cancel_button) layout.addLayout(button_layout) def _populate_list(self): """Populates the list widget with the provided links, grouped by post title.""" grouped_links = defaultdict(list) for link_info_item in self.links_data: post_title_for_group = link_info_item.get('title', 'Untitled Post') grouped_links[post_title_for_group].append(link_info_item) sorted_post_titles = sorted(grouped_links.keys(), key=lambda x: x.lower()) for post_title_key in sorted_post_titles: header_item = QListWidgetItem(f"{post_title_key}") header_item.setFlags(Qt.NoItemFlags) font = header_item.font() font.setBold(True) font.setPointSize(font.pointSize() + 1) header_item.setFont(font) self.links_list_widget.addItem(header_item) for link_info_data in grouped_links[post_title_key]: platform_display = link_info_data.get('platform', 'unknown').upper() display_text = f" [{platform_display}] {link_info_data['link_text']} ({link_info_data['url']})" item = QListWidgetItem(display_text) item.setData(Qt.UserRole, link_info_data) item.setFlags(item.flags() | Qt.ItemIsUserCheckable) item.setCheckState(Qt.Checked) self.links_list_widget.addItem(item) def _tr(self, key, default_text=""): """Helper to get translation based on current app language.""" if callable(get_translation) and self.parent_app: return get_translation(self.parent_app.current_selected_language, key, default_text) return default_text def _retranslate_ui(self): """Sets the text for all translatable UI elements.""" self.setWindowTitle(self._tr("download_external_links_dialog_title", "Download Selected External Links")) self.main_info_label.setText(self._tr("download_external_links_dialog_main_label", "Found {count} supported link(s)...").format(count=len(self.links_data))) self.select_all_button.setText(self._tr("select_all_button_text", "Select All")) self.deselect_all_button.setText(self._tr("deselect_all_button_text", "Deselect All")) self.download_button.setText(self._tr("download_selected_button_text", "Download Selected")) self.cancel_button.setText(self._tr("fav_posts_cancel_button", "Cancel")) def _apply_theme(self): """Applies the current theme from the parent application.""" is_dark_theme = self.parent_app and self.parent_app.current_theme == "dark" if is_dark_theme: scale = getattr(self.parent_app, 'scale_factor', 1) self.setStyleSheet(get_dark_theme(scale)) else: self.setStyleSheet("") header_color = Qt.cyan if is_dark_theme else Qt.blue for i in range(self.links_list_widget.count()): item = self.links_list_widget.item(i) if not item.flags() & Qt.ItemIsUserCheckable: item.setForeground(header_color) def _set_all_items_checked(self, check_state): """Sets the checked state for all checkable items in the list.""" for i in range(self.links_list_widget.count()): item = self.links_list_widget.item(i) if item.flags() & Qt.ItemIsUserCheckable: item.setCheckState(check_state) def _handle_download_selected(self): """Gathers selected links and emits the download_requested signal.""" selected_links = [] for i in range(self.links_list_widget.count()): item = self.links_list_widget.item(i) if item.flags() & Qt.ItemIsUserCheckable and item.checkState() == Qt.Checked and item.data(Qt.UserRole) is not None: selected_links.append(item.data(Qt.UserRole)) if selected_links: self.download_requested.emit(selected_links) self.accept() else: QMessageBox.information( self, self._tr("no_selection_title", "No Selection"), self._tr("no_selection_message_links", "Please select at least one link to download.") )