forked from CastroFidel/winehelper
added a file association button
This commit is contained in:
40
winehelper
40
winehelper
@@ -1179,6 +1179,16 @@ init_wineprefix () {
|
|||||||
# добавление ассоциаций файлов для запуска нативного приложения из wine
|
# добавление ассоциаций файлов для запуска нативного приложения из wine
|
||||||
# пример переменной: WH_XDG_OPEN="txt doc pdf"
|
# пример переменной: WH_XDG_OPEN="txt doc pdf"
|
||||||
check_variables WH_XDG_OPEN "0"
|
check_variables WH_XDG_OPEN "0"
|
||||||
|
# Сохраняем старые ассоциации, чтобы потом удалить те, что больше не нужны
|
||||||
|
local old_xdg_open
|
||||||
|
if [[ -f "$WINEPREFIX/last.conf" ]]; then
|
||||||
|
old_xdg_open=$(grep "WH_XDG_OPEN=" "$WINEPREFIX/last.conf" | sed -e 's/.*WH_XDG_OPEN="//' -e 's/"$//')
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Если переменная WH_XDG_OPEN была установлена извне (например, из GUI),
|
||||||
|
# то мы должны принудительно установить ее значение в "0", если она пуста,
|
||||||
|
# чтобы корректно удалить старые ассоциации.
|
||||||
|
[[ -z "$WH_XDG_OPEN" ]] && WH_XDG_OPEN="0"
|
||||||
local WRAPPER="${WH_TMP_DIR}/wh-xdg-open.sh"
|
local WRAPPER="${WH_TMP_DIR}/wh-xdg-open.sh"
|
||||||
local XDG_OPEN_REG="Software\Classes\xdg-open\shell\open\command"
|
local XDG_OPEN_REG="Software\Classes\xdg-open\shell\open\command"
|
||||||
if [[ $WH_XDG_OPEN != "0" ]] ; then
|
if [[ $WH_XDG_OPEN != "0" ]] ; then
|
||||||
@@ -1201,13 +1211,25 @@ init_wineprefix () {
|
|||||||
# добавляем новую команду xdg-open в реестр
|
# добавляем новую команду xdg-open в реестр
|
||||||
get_and_set_reg_file --add "$XDG_OPEN_REG" '@=' 'REG_SZ' "$WRAPPER %1" "system"
|
get_and_set_reg_file --add "$XDG_OPEN_REG" '@=' 'REG_SZ' "$WRAPPER %1" "system"
|
||||||
|
|
||||||
|
# Удаляем старые ассоциации, которых нет в новом списке
|
||||||
|
if [[ -n "$old_xdg_open" ]]; then
|
||||||
|
for old_ext in $old_xdg_open; do
|
||||||
|
if ! echo " $WH_XDG_OPEN " | grep -q " $old_ext "; then
|
||||||
|
get_and_set_reg_file --delete "Software\Classes\.$old_ext" '@='
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
# добавляем ассоциации файлов для запуска с помощью xdg-open
|
# добавляем ассоциации файлов для запуска с помощью xdg-open
|
||||||
for ext in $WH_XDG_OPEN ; do
|
for ext in $WH_XDG_OPEN ; do
|
||||||
get_and_set_reg_file --add "Software\Classes\.$ext" '@=' 'REG_SZ' "xdg-open" "system"
|
get_and_set_reg_file --add "Software\Classes\.$ext" '@=' 'REG_SZ' "xdg-open" "system"
|
||||||
done
|
done
|
||||||
print_info "Используются ассоциации с нативными приложениями для файлов: \"$WH_XDG_OPEN\""
|
print_info "Используются ассоциации с нативными приложениями для файлов: \"$WH_XDG_OPEN\""
|
||||||
else
|
else
|
||||||
# удаление команды xdg-open из реестра
|
# удаление всех ассоциаций
|
||||||
|
for old_ext in $old_xdg_open; do
|
||||||
|
get_and_set_reg_file --delete "Software\Classes\.$old_ext" '@='
|
||||||
|
done
|
||||||
get_and_set_reg_file --delete "$XDG_OPEN_REG" '@='
|
get_and_set_reg_file --delete "$XDG_OPEN_REG" '@='
|
||||||
# удаяем скрипт-обёртку
|
# удаяем скрипт-обёртку
|
||||||
try_remove_file "$WRAPPER"
|
try_remove_file "$WRAPPER"
|
||||||
@@ -1348,6 +1370,13 @@ init_database () {
|
|||||||
. "$WHDB_FILE"
|
. "$WHDB_FILE"
|
||||||
elif check_prefix_var && [[ -f "$WINEPREFIX/last.conf" ]] ; then
|
elif check_prefix_var && [[ -f "$WINEPREFIX/last.conf" ]] ; then
|
||||||
print_info "Найдены настройки из предыдущего использования префикса: $WINEPREFIX"
|
print_info "Найдены настройки из предыдущего использования префикса: $WINEPREFIX"
|
||||||
|
# Сохраняем значение WH_XDG_OPEN, если оно было установлено извне (например, из GUI).
|
||||||
|
# Это предотвращает его перезапись старым значением из last.conf.
|
||||||
|
# Используем `declare -p` для надежного определения, была ли переменная установлена,
|
||||||
|
# даже если она пустая (что означает "удалить все ассоциации").
|
||||||
|
if declare -p WH_XDG_OPEN &>/dev/null; then
|
||||||
|
wh_xdg_open_from_env="$WH_XDG_OPEN"
|
||||||
|
fi
|
||||||
cat "$WINEPREFIX/last.conf"
|
cat "$WINEPREFIX/last.conf"
|
||||||
. "$WINEPREFIX/last.conf"
|
. "$WINEPREFIX/last.conf"
|
||||||
else
|
else
|
||||||
@@ -1356,10 +1385,19 @@ init_database () {
|
|||||||
}
|
}
|
||||||
|
|
||||||
prepair_wine () {
|
prepair_wine () {
|
||||||
|
# Объявляем переменную здесь, чтобы она была доступна после вызова init_database
|
||||||
|
local wh_xdg_open_from_env
|
||||||
|
|
||||||
if [[ -n "$INSTALL_SCRIPT_NAME" ]]
|
if [[ -n "$INSTALL_SCRIPT_NAME" ]]
|
||||||
then print_info "Используются настройки из скрипта установки: $INSTALL_SCRIPT_NAME"
|
then print_info "Используются настройки из скрипта установки: $INSTALL_SCRIPT_NAME"
|
||||||
else init_database
|
else init_database
|
||||||
fi
|
fi
|
||||||
|
# Восстанавливаем значение WH_XDG_OPEN, если оно было установлено извне.
|
||||||
|
# Проверяем, была ли переменная сохранена, а не ее значение.
|
||||||
|
# Это позволяет корректно передать пустую строку.
|
||||||
|
if declare -p wh_xdg_open_from_env &>/dev/null; then
|
||||||
|
export WH_XDG_OPEN="$wh_xdg_open_from_env"
|
||||||
|
fi
|
||||||
init_wine_ver
|
init_wine_ver
|
||||||
init_wineprefix
|
init_wineprefix
|
||||||
use_winetricks
|
use_winetricks
|
||||||
|
@@ -12,7 +12,7 @@ import hashlib
|
|||||||
from functools import partial
|
from functools import partial
|
||||||
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,QPushButton, QLabel, QTabWidget, QTabBar,
|
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,QPushButton, QLabel, QTabWidget, QTabBar,
|
||||||
QTextEdit, QFileDialog, QMessageBox, QLineEdit, QCheckBox, QStackedWidget, QScrollArea, QFormLayout, QGroupBox, QRadioButton, QComboBox,
|
QTextEdit, QFileDialog, QMessageBox, QLineEdit, QCheckBox, QStackedWidget, QScrollArea, QFormLayout, QGroupBox, QRadioButton, QComboBox,
|
||||||
QListWidget, QListWidgetItem, QGridLayout, QFrame, QDialog, QTextBrowser, QInputDialog)
|
QListWidget, QListWidgetItem, QGridLayout, QFrame, QDialog, QTextBrowser, QInputDialog, QDialogButtonBox)
|
||||||
from PyQt5.QtCore import Qt, QProcess, QSize, QTimer, QProcessEnvironment, QPropertyAnimation, QEasingCurve
|
from PyQt5.QtCore import Qt, QProcess, QSize, QTimer, QProcessEnvironment, QPropertyAnimation, QEasingCurve
|
||||||
from PyQt5.QtGui import QIcon, QFont, QTextCursor, QPixmap, QPainter, QDesktopServices
|
from PyQt5.QtGui import QIcon, QFont, QTextCursor, QPixmap, QPainter, QDesktopServices
|
||||||
from PyQt5.QtNetwork import QLocalServer, QLocalSocket
|
from PyQt5.QtNetwork import QLocalServer, QLocalSocket
|
||||||
@@ -1332,6 +1332,75 @@ class CreatePrefixDialog(QDialog):
|
|||||||
|
|
||||||
self.accept()
|
self.accept()
|
||||||
|
|
||||||
|
class FileAssociationsDialog(QDialog):
|
||||||
|
"""Диалог для управления ассоциациями файлов (WH_XDG_OPEN)."""
|
||||||
|
|
||||||
|
def __init__(self, current_associations, parent=None):
|
||||||
|
super().__init__(parent)
|
||||||
|
self.setWindowTitle("Настройка ассоциаций файлов")
|
||||||
|
self.setMinimumWidth(450)
|
||||||
|
self.setModal(True)
|
||||||
|
|
||||||
|
self.new_associations = current_associations
|
||||||
|
|
||||||
|
layout = QVBoxLayout(self)
|
||||||
|
layout.setSpacing(10) # Добавляем вертикальный отступ между виджетами
|
||||||
|
|
||||||
|
info_label = QLabel(
|
||||||
|
"Укажите расширения файлов, которые должны открываться нативными<br>"
|
||||||
|
"приложениями Linux. Чтобы удалить все ассоциации, очистите поле.<br><br>"
|
||||||
|
"<b>Пример:</b> <code>pdf docx txt</code>"
|
||||||
|
)
|
||||||
|
info_label.setWordWrap(True)
|
||||||
|
info_label.setTextFormat(Qt.RichText)
|
||||||
|
layout.addWidget(info_label)
|
||||||
|
|
||||||
|
self.associations_edit = QLineEdit()
|
||||||
|
# Если ассоциации не заданы (значение "0"), поле будет пустым, чтобы показать подсказку
|
||||||
|
if current_associations != "0":
|
||||||
|
self.associations_edit.setText(current_associations)
|
||||||
|
|
||||||
|
self.associations_edit.setPlaceholderText("Введите расширения через пробел...")
|
||||||
|
layout.addWidget(self.associations_edit)
|
||||||
|
|
||||||
|
# Запрещенные расширения
|
||||||
|
forbidden_label = QLabel(
|
||||||
|
"<small><b>Запрещено использовать:</b> cpl, dll, exe, lnk, msi</small>"
|
||||||
|
)
|
||||||
|
forbidden_label.setTextFormat(Qt.RichText) # Включаем обработку HTML
|
||||||
|
layout.addWidget(forbidden_label)
|
||||||
|
|
||||||
|
button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
|
||||||
|
button_box.accepted.connect(self.validate_and_accept)
|
||||||
|
button_box.rejected.connect(self.reject)
|
||||||
|
layout.addWidget(button_box)
|
||||||
|
|
||||||
|
def validate_and_accept(self):
|
||||||
|
"""Проверяет введенные данные перед закрытием."""
|
||||||
|
forbidden_extensions = {"cpl", "dll", "exe", "lnk", "msi"}
|
||||||
|
|
||||||
|
# Получаем введенные расширения, очищаем от лишних пробелов
|
||||||
|
input_text = self.associations_edit.text().lower().strip()
|
||||||
|
entered_extensions = {ext.strip() for ext in input_text.split() if ext.strip()}
|
||||||
|
|
||||||
|
found_forbidden = entered_extensions.intersection(forbidden_extensions)
|
||||||
|
|
||||||
|
if found_forbidden:
|
||||||
|
msg_box = QMessageBox(self)
|
||||||
|
msg_box.setIcon(QMessageBox.Warning)
|
||||||
|
msg_box.setWindowTitle("Недопустимые расширения")
|
||||||
|
msg_box.setTextFormat(Qt.RichText)
|
||||||
|
msg_box.setText(
|
||||||
|
"Следующие расширения запрещены и не могут быть использованы:<br><br>"
|
||||||
|
f"<b>{', '.join(sorted(list(found_forbidden)))}</b>"
|
||||||
|
)
|
||||||
|
msg_box.exec_()
|
||||||
|
return
|
||||||
|
|
||||||
|
# Сохраняем результат в виде отсортированной строки
|
||||||
|
self.new_associations = " ".join(sorted(list(entered_extensions)))
|
||||||
|
self.accept()
|
||||||
|
|
||||||
class ComponentVersionSelectionDialog(QDialog):
|
class ComponentVersionSelectionDialog(QDialog):
|
||||||
"""Диалог для выбора версии компонента (DXVK, VKD3D)."""
|
"""Диалог для выбора версии компонента (DXVK, VKD3D)."""
|
||||||
|
|
||||||
@@ -2119,6 +2188,13 @@ class WineHelperGUI(QMainWindow):
|
|||||||
self.vkd3d_manage_button.setToolTip("Установка или удаление определенной версии vkd3d-proton в префиксе.")
|
self.vkd3d_manage_button.setToolTip("Установка или удаление определенной версии vkd3d-proton в префиксе.")
|
||||||
management_layout.addWidget(self.vkd3d_manage_button, 5, 1)
|
management_layout.addWidget(self.vkd3d_manage_button, 5, 1)
|
||||||
|
|
||||||
|
self.file_associations_button = QPushButton("Ассоциации файлов")
|
||||||
|
self.file_associations_button.setMinimumHeight(32)
|
||||||
|
self.file_associations_button.clicked.connect(self.open_file_associations_manager)
|
||||||
|
self.file_associations_button.setToolTip(
|
||||||
|
"Настройка открытия определенных типов файлов с помощью нативных приложений Linux.")
|
||||||
|
management_layout.addWidget(self.file_associations_button, 6, 0, 1, 2)
|
||||||
|
|
||||||
# --- Правая сторона: Информационный блок и кнопки установки ---
|
# --- Правая сторона: Информационный блок и кнопки установки ---
|
||||||
right_column_widget = QWidget()
|
right_column_widget = QWidget()
|
||||||
right_column_layout = QVBoxLayout(right_column_widget)
|
right_column_layout = QVBoxLayout(right_column_widget)
|
||||||
@@ -2151,7 +2227,7 @@ class WineHelperGUI(QMainWindow):
|
|||||||
right_column_layout.setStretch(0, 1) # Информационное окно растягивается
|
right_column_layout.setStretch(0, 1) # Информационное окно растягивается
|
||||||
right_column_layout.setStretch(1, 0) # Группа кнопок не растягивается
|
right_column_layout.setStretch(1, 0) # Группа кнопок не растягивается
|
||||||
|
|
||||||
management_layout.addWidget(right_column_widget, 0, 2, 6, 1)
|
management_layout.addWidget(right_column_widget, 0, 2, 7, 1)
|
||||||
|
|
||||||
management_layout.setColumnStretch(0, 1)
|
management_layout.setColumnStretch(0, 1)
|
||||||
management_layout.setColumnStretch(1, 1)
|
management_layout.setColumnStretch(1, 1)
|
||||||
@@ -2358,8 +2434,9 @@ class WineHelperGUI(QMainWindow):
|
|||||||
"VKD3D_VER": ("Версия VKD3D", lambda v: v if v else "Не установлено"),
|
"VKD3D_VER": ("Версия VKD3D", lambda v: v if v else "Не установлено"),
|
||||||
"WINEESYNC": ("ESync", lambda v: "Включен" if v == "1" else "Выключен"),
|
"WINEESYNC": ("ESync", lambda v: "Включен" if v == "1" else "Выключен"),
|
||||||
"WINEFSYNC": ("FSync", lambda v: "Включен" if v == "1" else "Выключен"),
|
"WINEFSYNC": ("FSync", lambda v: "Включен" if v == "1" else "Выключен"),
|
||||||
|
"WH_XDG_OPEN": ("Ассоциации файлов", lambda v: v if v and v != "0" else "Не заданы"),
|
||||||
}
|
}
|
||||||
display_order = ["WINEPREFIX", "WINEARCH", "WH_WINE_USE", "BASE_PFX", "DXVK_VER", "VKD3D_VER", "WINEESYNC", "WINEFSYNC"]
|
display_order = ["WINEPREFIX", "WINEARCH", "WH_WINE_USE", "BASE_PFX", "DXVK_VER", "VKD3D_VER", "WINEESYNC", "WINEFSYNC", "WH_XDG_OPEN"]
|
||||||
|
|
||||||
html_content = f'<p style="line-height: 1.3; font-size: 9pt;">'
|
html_content = f'<p style="line-height: 1.3; font-size: 9pt;">'
|
||||||
html_content += f"<b>Имя:</b> {html.escape(prefix_name)}<br>"
|
html_content += f"<b>Имя:</b> {html.escape(prefix_name)}<br>"
|
||||||
@@ -2659,6 +2736,64 @@ class WineHelperGUI(QMainWindow):
|
|||||||
if exit_code == 0:
|
if exit_code == 0:
|
||||||
self.update_prefix_info_display(prefix_name)
|
self.update_prefix_info_display(prefix_name)
|
||||||
|
|
||||||
|
def open_file_associations_manager(self):
|
||||||
|
"""Открывает диалог для управления ассоциациями файлов."""
|
||||||
|
prefix_name = self.current_managed_prefix_name
|
||||||
|
if not prefix_name:
|
||||||
|
QMessageBox.warning(self, "Ошибка", "Сначала выберите префикс.")
|
||||||
|
return
|
||||||
|
|
||||||
|
current_associations = self._get_prefix_component_version(prefix_name, "WH_XDG_OPEN") or ""
|
||||||
|
|
||||||
|
dialog = FileAssociationsDialog(current_associations, self)
|
||||||
|
if dialog.exec_() == QDialog.Accepted:
|
||||||
|
new_associations = dialog.new_associations
|
||||||
|
# Запускаем обновление, только если значение изменилось
|
||||||
|
if new_associations != current_associations:
|
||||||
|
self.run_update_associations_command(prefix_name, new_associations)
|
||||||
|
|
||||||
|
def run_update_associations_command(self, prefix_name, new_associations):
|
||||||
|
"""Выполняет команду обновления ассоциаций файлов."""
|
||||||
|
prefix_path = os.path.join(Var.USER_WORK_PATH, "prefixes", prefix_name)
|
||||||
|
|
||||||
|
self.command_dialog = QDialog(self)
|
||||||
|
self.command_dialog.setWindowTitle("Обновление ассоциаций файлов")
|
||||||
|
self.command_dialog.setMinimumSize(750, 400)
|
||||||
|
self.command_dialog.setModal(True)
|
||||||
|
self.command_dialog.setWindowFlags(self.command_dialog.windowFlags() & ~Qt.WindowCloseButtonHint)
|
||||||
|
|
||||||
|
layout = QVBoxLayout()
|
||||||
|
self.command_log_output = QTextEdit()
|
||||||
|
self.command_log_output.setReadOnly(True)
|
||||||
|
self.command_log_output.setFont(QFont('DejaVu Sans Mono', 10))
|
||||||
|
layout.addWidget(self.command_log_output)
|
||||||
|
|
||||||
|
self.command_close_button = QPushButton("Закрыть")
|
||||||
|
self.command_close_button.setEnabled(False)
|
||||||
|
self.command_close_button.clicked.connect(self.command_dialog.close)
|
||||||
|
layout.addWidget(self.command_close_button)
|
||||||
|
self.command_dialog.setLayout(layout)
|
||||||
|
|
||||||
|
self.command_process = QProcess(self.command_dialog)
|
||||||
|
self.command_process.setProcessChannelMode(QProcess.MergedChannels)
|
||||||
|
self.command_process.readyReadStandardOutput.connect(self._handle_command_output)
|
||||||
|
self.command_process.finished.connect(
|
||||||
|
lambda exit_code, exit_status: self._handle_component_install_finished(
|
||||||
|
prefix_name, exit_code, exit_status
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
env = QProcessEnvironment.systemEnvironment()
|
||||||
|
env.insert("WINEPREFIX", prefix_path)
|
||||||
|
# Устанавливаем новую переменную окружения для скрипта
|
||||||
|
env.insert("WH_XDG_OPEN", new_associations)
|
||||||
|
self.command_process.setProcessEnvironment(env)
|
||||||
|
|
||||||
|
args = ["init-prefix"]
|
||||||
|
self.command_log_output.append(f"Выполнение: {shlex.quote(self.winehelper_path)} {' '.join(shlex.quote(a) for a in args)}")
|
||||||
|
self.command_process.start(self.winehelper_path, args)
|
||||||
|
self.command_dialog.exec_()
|
||||||
|
|
||||||
def create_launcher_for_prefix(self):
|
def create_launcher_for_prefix(self):
|
||||||
"""
|
"""
|
||||||
Открывает диалог для создания ярлыка для приложения внутри выбранного префикса.
|
Открывает диалог для создания ярлыка для приложения внутри выбранного префикса.
|
||||||
|
Reference in New Issue
Block a user