1
0
Fork 0
mirror of synced 2024-07-01 20:50:34 +12:00
Rare/rare/components/tabs/settings/widgets/env_vars.py
2023-02-04 17:38:07 +02:00

311 lines
13 KiB
Python

from logging import getLogger
from PyQt5.QtCore import Qt, QFileSystemWatcher
from PyQt5.QtGui import QFont
from PyQt5.QtWidgets import QGroupBox, QTableWidgetItem, QMessageBox, QPushButton, QHeaderView, QFrame
from rare.shared import LegendaryCoreSingleton
from rare.ui.components.tabs.settings.widgets.env_vars import Ui_EnvVars
from rare.utils import config_helper
from rare.utils.misc import icon
logger = getLogger("EnvVars")
class EnvVars(QGroupBox, Ui_EnvVars):
def __init__(self, parent):
super(EnvVars, self).__init__(parent=parent)
self.setupUi(self)
self.app_name = None
self.core = LegendaryCoreSingleton()
self.latest_item = None
self.list_of_readonly = [
"STEAM_COMPAT_DATA_PATH",
"STEAM_COMPAT_CLIENT_INSTALL_PATH",
"WINEPREFIX",
"DXVK_HUD",
"MANGOHUD_CONFIG",
]
self.warn_msg = self.tr("Readonly, please edit this via the appropriate setting above.")
self.setup_file_watcher()
self.env_vars_table.cellChanged.connect(self.update_env_vars)
self.env_vars_table.verticalHeader().sectionClicked.connect(self.remove_row)
# We use this function to keep an eye on the config.
# When the user uses for example the wineprefix settings, we need to update the table.
# With this function, when the config file changes, we update the table.
def setup_file_watcher(self):
self.config_file_watcher = QFileSystemWatcher([str(self.core.lgd.config_path)], self)
self.config_file_watcher.fileChanged.connect(self.import_env_vars)
def append_row(self):
# If the last row is not None, we insert a new one and set the correct icon.
row_count = self.env_vars_table.rowCount()
if row_count == 0:
self.env_vars_table.insertRow(0)
trash_icon = QTableWidgetItem()
trash_icon.setIcon(icon("mdi.delete", "ei.minus"))
self.env_vars_table.setVerticalHeaderItem(row_count, trash_icon)
return
last_item = self.env_vars_table.item(self.env_vars_table.rowCount() - 1, 0)
if last_item is not None:
self.env_vars_table.insertRow(row_count)
trash_icon = QTableWidgetItem()
trash_icon.setIcon(icon("mdi.delete", "ei.minus"))
self.env_vars_table.setVerticalHeaderItem(row_count, trash_icon)
def import_env_vars(self):
self.env_vars_table.blockSignals(True)
self.env_vars_table.clearContents()
# If the config file doesnt have an env var section, we just set RowCount to 1 and return.
if not self.core.lgd.config.has_section(f"{self.app_name}.env"):
self.env_vars_table.setRowCount(1)
trash_icon = QTableWidgetItem()
trash_icon.setIcon(icon("mdi.delete", "ei.minus"))
self.env_vars_table.setVerticalHeaderItem(0, trash_icon)
self.env_vars_table.blockSignals(False)
return
# We count how many keys we have and insert new lines
# (as many as we need).
self.env_vars_table.setRowCount(len(self.core.lgd.config[f"{self.app_name}.env"]) + 1)
# Each iteration we have to create a new QTableWidgetItem object,
# else we segfault. (For using the same object in multiple references.)
for i, (key, value) in enumerate(self.core.lgd.config[f"{self.app_name}.env"].items()):
trash_icon = QTableWidgetItem()
trash_icon.setIcon(icon("mdi.delete", "ei.minus"))
self.env_vars_table.setVerticalHeaderItem(i, trash_icon)
font = QFont("Monospace")
font.setStyleHint(QFont.Monospace)
key_item = QTableWidgetItem()
key_item.setText(key)
key_item.setFont(font)
self.env_vars_table.setItem(i, 0, key_item)
value_item = QTableWidgetItem()
value_item.setFont(font)
value_item.setText(value)
self.env_vars_table.setItem(i, 1, value_item)
if key in self.list_of_readonly:
key_item.setFlags(key_item.flags() ^ Qt.ItemIsEnabled)
key_item.setToolTip(self.warn_msg)
value_item.setFlags(value_item.flags() ^ Qt.ItemIsEnabled)
value_item.setToolTip(self.warn_msg)
trash_icon = QTableWidgetItem()
trash_icon.setIcon(icon("mdi.delete", "ei.minus"))
self.env_vars_table.setVerticalHeaderItem(self.env_vars_table.rowCount() - 1, trash_icon)
self.env_vars_table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
self.env_vars_table.blockSignals(False)
def update_env_vars(self, row, column):
self.config_file_watcher.removePath(str(self.core.lgd.config_path))
row_count = self.env_vars_table.rowCount()
key_item = self.env_vars_table.item(row, 0)
value_item = self.env_vars_table.item(row, 1)
if key_item is not None and not key_item.text():
try:
list_of_config_keys = list(self.core.lgd.config[f"{self.app_name}.env"].keys())
except KeyError:
list_of_config_keys = []
try:
config_helper.remove_option(f"{self.app_name}.env", list_of_config_keys[row])
except IndexError:
# Item hasnt been saved to the config yet.
pass
self.env_vars_table.removeRow(key_item.row())
self.append_row()
return
# get all config keys
try:
list_of_config_keys = list(self.core.lgd.config[f"{self.app_name}.env"].keys())
except KeyError:
list_of_config_keys = []
# get all table keys
list_of_keys_in_table = []
for i in range(row_count):
item = self.env_vars_table.item(i, 0)
if item:
list_of_keys_in_table.append(item.text())
missing_item = list(set(list_of_config_keys) - set(list_of_keys_in_table))
if len(missing_item) != 0:
config_helper.remove_option(f"{self.app_name}.env", missing_item[0])
# A env var always needs to have a key.
# If it's none, we return.
if key_item is None:
return
if key_item.text() in self.list_of_readonly:
error_dialog = QMessageBox()
error_dialog.setText(
self.tr("Please don't manually add this environment variable. Use the appropriate game setting above."))
error_dialog.exec()
key_item.setText("")
if value_item is not None:
value_item.setText("")
config_helper.remove_option(f"{self.app_name}.env", key_item.text())
return
if key_item.text():
if "=" in key_item.text():
error_dialog = QMessageBox()
error_dialog.setText(
self.tr("Please don't use an equal sign in an env var."))
error_dialog.exec()
self.env_vars_table.removeRow(row)
self.append_row()
return
if key_item.text() in list_of_config_keys and column == 0:
ask_user = QMessageBox()
ask_user.setText(
self.tr("The config already contains this environment variable."))
ask_user.setInformativeText(
self.tr("Do you want to keep the newer one or the older one?"))
ask_user.addButton(QPushButton("Keep the newer one"), QMessageBox.YesRole)
ask_user.addButton(QPushButton("Keep the older one"), QMessageBox.NoRole)
response = ask_user.exec()
if response == 0:
if value_item is not None:
config_helper.add_option(f"{self.app_name}.env", key_item.text(), value_item.text())
else:
config_helper.add_option(f"{self.app_name}.env", key_item.text(), "")
item_to_safe = self.env_vars_table.findItems(key_item.text(), Qt.MatchExactly)
# aznd:
# This is to fix an issue where the user updates a env var, thats above the older one.
# so say if you have two env vars:
# something = newkey
# yes = oldkey
# if the user updates the something key to yes, it would delete the wrong row,
# since item_to_safe[0] is the first search result. but we dont want to delete that,
# we want to delete the second result.
# we use this simple if check to find out which case we have.
if key_item.row() < item_to_safe[0].row():
self.env_vars_table.removeRow(item_to_safe[0].row())
elif key_item.row() > item_to_safe[0].row():
self.env_vars_table.removeRow(item_to_safe[0].row())
else:
self.env_vars_table.removeRow(item_to_safe[1].row())
self.append_row()
return
elif response == 1:
self.env_vars_table.removeRow(row)
self.append_row()
return
# When the value_item is None, we just use an empty string for the value.
if value_item is None:
if self.latest_item in list_of_config_keys:
config_helper.remove_option(f"{self.app_name}.env", self.latest_item)
config_helper.save_config()
config_helper.add_option(f"{self.app_name}.env", key_item.text(), "")
config_helper.save_config()
else:
if self.latest_item in list_of_config_keys:
config_helper.remove_option(f"{self.app_name}.env", self.latest_item)
config_helper.save_config()
config_helper.add_option(
f"{self.app_name}.env",
key_item.text(),
value_item.text()
)
config_helper.save_config()
self.append_row()
self.config_file_watcher.addPath(str(self.core.lgd.config_path))
def remove_row(self, index):
self.config_file_watcher.removePath(str(self.core.lgd.config_path))
key_item = self.env_vars_table.item(index, 0)
value_item = self.env_vars_table.item(index, 1)
if key_item is None:
if value_item is not None:
value_item.setText("")
return
# If the user tries to delete one of the readonly vars, we return immediately.
if key_item.text() in self.list_of_readonly:
return
if key_item is not None:
self.env_vars_table.removeRow(index)
try:
list_of_keys = []
for key in self.core.lgd.config[f"{self.app_name}.env"]:
list_of_keys.append(key)
config_helper.remove_option(f"{self.app_name}.env", list_of_keys[index])
except (KeyError, IndexError):
pass
self.config_file_watcher.addPath(str(self.core.lgd.config_path))
def check_if_item(self, item: QTableWidgetItem) -> bool:
item_to_check = self.env_vars_table.findItems(item.text(), Qt.MatchExactly)
if item_to_check[0].column() == 0:
return False
return True
def keyPressEvent(self, e):
if e.key() == Qt.Key_Delete or e.key() == Qt.Key_Backspace:
selected_items = self.env_vars_table.selectedItems()
if len(selected_items) == 0:
return
item_in_table = self.env_vars_table.findItems(selected_items[0].text(), Qt.MatchExactly)
# Our first selection is in column 0. So, we have to find out if the user
# only selected keys, or keys and values. we use the check_if_item func
if item_in_table[0].column() == 0:
which_index_to_use = 1
if len(selected_items) == 1:
which_index_to_use = 0
if self.check_if_item(selected_items[which_index_to_use]):
# User selected keys and values, so we skip the values
for i in selected_items[::2]:
if i:
config_helper.remove_option(f"{self.app_name}.env", i.text())
self.env_vars_table.removeRow(i.row())
self.append_row()
else:
# user only selected keys
for i in selected_items:
if i:
config_helper.remove_option(f"{self.app_name}.env", i.text())
self.env_vars_table.removeRow(i.row())
self.append_row()
# User only selected values, so we just set the text to ""
elif item_in_table[0].column() == 1:
[i.setText("") for i in selected_items]
elif e.key() == Qt.Key_Escape:
e.ignore()
def update_game(self, app_name):
self.app_name = app_name
self.import_env_vars()