diff --git a/winehelper b/winehelper index 202a08de..94527600 100755 --- a/winehelper +++ b/winehelper @@ -1700,7 +1700,7 @@ select_wine_version() { system_wine_version=$(wine --version 2>/dev/null) [[ -n "$system_wine_version" ]] && system_wine_display_name="$system_wine_version" fi - options+=("--- System ---") + options+=("--- SYSTEM ---") options+=("$system_wine_display_name") # --- Other versions from sha256sum.list --- @@ -1718,13 +1718,13 @@ select_wine_version() { } while IFS= read -r line; do - if [[ "$line" =~ ^#+[[:space:]]([^#[:space:]]+)[[:space:]]#* ]]; then + if [[ "$line" =~ ^#+[[:space:]](.*[^#[:space:]])[[:space:]]#* ]] ; then flush_group current_group="${BASH_REMATCH[1]}" # Отображаем только группы, которые являются сборками WINE или PROTON case "$current_group" in - WINE|WINE_LG|PROTON_LG|PROTON_STEAM) - local pretty_key=$(echo "$current_group" | tr '_' ' ' | sed -e "s/\b\(.\)/\u\1/g") + "WINE WOW64"|"WINE AMD64"|"WINE I586") + local pretty_key="$current_group" options+=("--- $pretty_key ---") ;; *) diff --git a/winehelper_gui.py b/winehelper_gui.py index 45841b53..93362c32 100644 --- a/winehelper_gui.py +++ b/winehelper_gui.py @@ -1129,28 +1129,28 @@ class WineVersionSelectionDialog(QDialog): self.search_edit.textChanged.connect(self.filter_versions) main_layout.addWidget(self.search_edit) - self.version_tabs = QTabWidget() - main_layout.addWidget(self.version_tabs) + self.main_tabs = QTabWidget() + main_layout.addWidget(self.main_tabs) self.load_versions() def load_versions(self): """Запускает процесс получения списка версий Wine.""" - self.version_tabs.clear() + self.main_tabs.clear() loading_widget = QWidget() loading_layout = QVBoxLayout(loading_widget) status_label = QLabel("Загрузка, пожалуйста, подождите...") status_label.setAlignment(Qt.AlignCenter) loading_layout.addWidget(status_label) - self.version_tabs.addTab(loading_widget, "Загрузка...") - self.version_tabs.setEnabled(False) + self.main_tabs.addTab(loading_widget, "Загрузка...") + self.main_tabs.setEnabled(False) QApplication.processEvents() self._parse_sha256_list() self.populate_ui() - self.version_tabs.setEnabled(True) + self.main_tabs.setEnabled(True) def _parse_sha256_list(self): """Парсит sha256sum.list для получения списка версий.""" @@ -1170,10 +1170,10 @@ class WineVersionSelectionDialog(QDialog): if not line: continue - match = re.match(r'^#+\s*([^#]+?)\s*#*$', line) + match = re.match(r'^#+\s*(.*?)\s*#*$', line) if match: - group_name = match.group(1) - allowed_groups = {"WINE", "WINE_LG", "PROTON_LG", "PROTON_STEAM"} + group_name = match.group(1).strip() + allowed_groups = {"WINE WOW64", "WINE AMD64", "WINE I586"} # Отображаем только группы, которые являются сборками WINE или PROTON if group_name in allowed_groups: current_group = group_name @@ -1194,9 +1194,59 @@ class WineVersionSelectionDialog(QDialog): QMessageBox.warning(self, "Ошибка", f"Не удалось прочитать файл версий:\n{e}") self.wine_versions_data = {} + def _get_installed_versions(self): + """Возвращает список локально установленных версий Wine.""" + dist_path = os.path.join(Var.USER_WORK_PATH, "dist") + if not os.path.isdir(dist_path): + return [] + try: + return sorted([ + name for name in os.listdir(dist_path) + if os.path.isdir(os.path.join(dist_path, name)) + ], reverse=True) + except OSError: + return [] + def populate_ui(self): """Заполняет UI отфильтрованными версиями.""" - self.version_tabs.clear() + self.main_tabs.clear() + + # --- Вкладка "Установленные" --- + installed_tab = QWidget() + self.main_tabs.addTab(installed_tab, "Установленные") + installed_layout = QVBoxLayout(installed_tab) + installed_scroll_area = QScrollArea() + installed_scroll_area.setWidgetResizable(True) + installed_layout.addWidget(installed_scroll_area) + installed_content = QWidget() + installed_scroll_area.setWidget(installed_content) + self.installed_grid_layout = QGridLayout(installed_content) + self.installed_grid_layout.setAlignment(Qt.AlignTop) + + installed_versions_for_grid = [] + # Системная версия + if shutil.which('wine'): + try: + result = subprocess.run(['wine', '--version'], capture_output=True, text=True, check=True, encoding='utf-8') + self.system_wine_display_name = result.stdout.strip() + except (FileNotFoundError, subprocess.CalledProcessError) as e: + print(f"Не удалось получить версию системного wine: {e}") + self.system_wine_display_name = "Системная версия" + installed_versions_for_grid.append((self.system_wine_display_name, "system")) + + # Локально установленные версии + local_versions = self._get_installed_versions() + installed_versions_for_grid.extend(local_versions) + + self._create_grid_buttons(self.installed_grid_layout, installed_versions_for_grid) + + # --- Вкладка "Скачать" --- + download_tab = QWidget() + download_layout = QVBoxLayout(download_tab) + self.version_tabs = QTabWidget() # Вложенные табы для категорий + download_layout.addWidget(self.version_tabs) + download_tab.setLayout(download_layout) + self.main_tabs.addTab(download_tab, "Скачать") if not self.wine_versions_data: error_widget = QWidget() @@ -1205,55 +1255,33 @@ class WineVersionSelectionDialog(QDialog): error_label.setAlignment(Qt.AlignCenter) error_layout.addWidget(error_label) self.version_tabs.addTab(error_widget, "Ошибка") + self.filter_versions() return + # --- Фильтрация и сортировка групп в зависимости от архитектуры --- is_win64 = self.architecture == "win64" - re_32bit = re.compile(r'i[3-6]86|x86(?!_64)') - re_64bit = re.compile(r'amd64|x86_64|wow64') - # --- System Tab --- - if shutil.which('wine'): - self.system_wine_display_name = "Системная версия" - try: - # Пытаемся получить версию системного wine - result = subprocess.run(['wine', '--version'], capture_output=True, text=True, check=True, encoding='utf-8') - version_line = result.stdout.strip() - # Вывод обычно "wine-X.Y.Z" - self.system_wine_display_name = version_line - except (FileNotFoundError, subprocess.CalledProcessError) as e: - print(f"Не удалось получить версию системного wine: {e}") - # Если wine возвращает ошибку, используем имя по умолчанию "Системная версия" + if is_win64: + allowed_groups_for_arch = {"WINE AMD64", "WINE WOW64"} + tab_order = ["WINE AMD64", "WINE WOW64"] + else: # win32 + allowed_groups_for_arch = {"WINE I586","WINE AMD64", "WINE WOW64"} + tab_order = ["WINE I586","WINE AMD64", "WINE WOW64"] - self._create_version_tab("Системный", [(self.system_wine_display_name, "system")]) - - # Определяем желаемый порядок вкладок - tab_order = ["WINE", "WINE_LG", "PROTON_LG", "PROTON_STEAM"] - # Сортируем ключи в соответствии с заданным порядком - group_keys = sorted(self.wine_versions_data.keys(), key=lambda k: tab_order.index(k) if k in tab_order else len(tab_order)) + # Получаем только те ключи, которые есть в данных и разрешены для данной архитектуры + available_keys = [key for key in self.wine_versions_data.keys() if key in allowed_groups_for_arch] + # Сортируем доступные ключи в соответствии с заданным порядком + group_keys = sorted(available_keys, key=lambda k: tab_order.index(k)) for key in group_keys: versions = self.wine_versions_data.get(key, []) - - filtered_versions = [] - for name in sorted(versions, reverse=True): - if is_win64: - if re_64bit.search(name) or not re_32bit.search(name): - filtered_versions.append(name) - else: - filtered_versions.append(name) - - if not filtered_versions: + if not versions: continue - - pretty_key = key.replace('_', ' ').title() - if key.endswith('_LG'): - pretty_key = pretty_key.replace(' Lg', ' LG') - - self._create_version_tab(pretty_key, filtered_versions) + self._create_version_tab(key, sorted(versions, reverse=True)) self.filter_versions() - def _create_version_tab(self, title, versions_list): + def _create_version_tab(self, title, versions_list, is_download_tab=True): """Создает вкладку с сеткой кнопок для переданного списка версий.""" tab_page = QWidget() tab_layout = QVBoxLayout(tab_page) @@ -1269,6 +1297,13 @@ class WineVersionSelectionDialog(QDialog): grid_layout = QGridLayout(scroll_content) grid_layout.setAlignment(Qt.AlignTop) + self._create_grid_buttons(grid_layout, versions_list, is_download_tab=is_download_tab) + self.version_tabs.addTab(tab_page, title) + + def _create_grid_buttons(self, grid_layout, versions_list, is_download_tab=False): + is_win64_prefix = self.architecture == "win64" + re_32bit_version = re.compile(r'i[3-6]86|i586') + num_columns = 3 row, col = 0, 0 for version_data in versions_list: @@ -1279,36 +1314,58 @@ class WineVersionSelectionDialog(QDialog): btn = QPushButton(display_name) btn.clicked.connect(partial(self.on_version_selected, value_name)) + + # Выделяем системную версию Wine + if value_name == "system": + btn.setText(f"{display_name} (Системный)") + btn.setToolTip("Системная версия Wine, установленная в вашей ОС.") + + # Отключение 32-битных версий Wine при выбранном 64-битного префикса (вкладка "Установленные") + if not is_download_tab and is_win64_prefix: + if re_32bit_version.search(value_name): + btn.setEnabled(False) + btn.setToolTip("Эта 32-битная версия Wine несовместима с 64-битным префиксом.") + grid_layout.addWidget(btn, row, col) col += 1 if col >= num_columns: col = 0 row += 1 - self.version_tabs.addTab(tab_page, title) - def filter_versions(self): """Фильтрует видимость кнопок версий на основе текста поиска.""" search_text = self.search_edit.text().lower() - for i in range(self.version_tabs.count()): - tab_widget = self.version_tabs.widget(i) - # The grid layout is inside a scroll area content widget - grid_layout = tab_widget.findChild(QGridLayout) - if not grid_layout: - continue + # Фильтр для вкладки "Установленные" + if hasattr(self, 'installed_grid_layout'): + self._filter_grid(self.installed_grid_layout, search_text) - any_visible_in_tab = False - for j in range(grid_layout.count()): - btn_widget = grid_layout.itemAt(j).widget() + # Фильтр для вкладок "Скачать" + if hasattr(self, 'version_tabs'): + for i in range(self.version_tabs.count()): + tab_widget = self.version_tabs.widget(i) + grid_layout = tab_widget.findChild(QGridLayout) + if grid_layout: + any_visible = self._filter_grid(grid_layout, search_text) + self.version_tabs.setTabEnabled(i, any_visible) + + def _filter_grid(self, grid_layout, search_text): + """Helper-функция для фильтрации кнопок в одной сетке.""" + any_visible_in_grid = False + if not grid_layout: + return False + + for j in range(grid_layout.count()): + item = grid_layout.itemAt(j) + if item: + btn_widget = item.widget() if isinstance(btn_widget, QPushButton): is_match = search_text in btn_widget.text().lower() btn_widget.setVisible(is_match) if is_match: - any_visible_in_tab = True + any_visible_in_grid = True - # Enable/disable tab based on content - self.version_tabs.setTabEnabled(i, any_visible_in_tab) + return any_visible_in_grid def on_version_selected(self, version_name): """Обрабатывает выбор версии."""