diff --git a/src/Mod/AddonManager/AddonManager.py b/src/Mod/AddonManager/AddonManager.py index 7253877b29..13c2440cf8 100644 --- a/src/Mod/AddonManager/AddonManager.py +++ b/src/Mod/AddonManager/AddonManager.py @@ -755,6 +755,8 @@ class CommandAddonManager: real_install_succeeded, errors = macro.install(self.macro_repo_dir) if not real_install_succeeded: failed = True + else: + utils.update_macro_installation_details(repo) if not failed: message = translate( diff --git a/src/Mod/AddonManager/addonmanager_utilities.py b/src/Mod/AddonManager/addonmanager_utilities.py index 5cc3ca5950..1ab5fafc15 100644 --- a/src/Mod/AddonManager/addonmanager_utilities.py +++ b/src/Mod/AddonManager/addonmanager_utilities.py @@ -28,6 +28,7 @@ import ssl from typing import Union import urllib +from urllib.request import Request from urllib.error import URLError from urllib.parse import urlparse from http.client import HTTPResponse @@ -294,14 +295,75 @@ def fix_relative_links(text, base_url): def warning_color_string() -> str: + """A shade of red, adapted to darkmode if possible. Targets a minimum 7:1 contrast ratio.""" + warningColorString = "rgb(255,0,0)" if hasattr(QtWidgets.QApplication.instance(), "styleSheet"): # Qt 5.9 doesn't give a QApplication instance, so can't give the stylesheet info if "dark" in QtWidgets.QApplication.instance().styleSheet().lower(): - warningColorString = "rgb(255,50,50)" + warningColorString = "rgb(255,105,97)" else: - warningColorString = "rgb(200,0,0)" + warningColorString = "rgb(215,0,21)" return warningColorString +def bright_color_string() -> str: + """A shade of green, adapted to darkmode if possible. Targets a minimum 7:1 contrast ratio.""" + + brightColorString = "rgb(0,255,0)" + if hasattr(QtWidgets.QApplication.instance(), "styleSheet"): + # Qt 5.9 doesn't give a QApplication instance, so can't give the stylesheet info + if "dark" in QtWidgets.QApplication.instance().styleSheet().lower(): + brightColorString = "rgb(48,219,91)" + else: + brightColorString = "rgb(36,138,61)" + return brightColorString + + +def attention_color_string() -> str: + """A shade of orange, adapted to darkmode if possible. Targets a minimum 7:1 contrast ratio.""" + + attentionColorString = "rgb(255,149,0)" + if hasattr(QtWidgets.QApplication.instance(), "styleSheet"): + # Qt 5.9 doesn't give a QApplication instance, so can't give the stylesheet info + if "dark" in QtWidgets.QApplication.instance().styleSheet().lower(): + attentionColorString = "rgb(255,179,64)" + else: + attentionColorString = "rgb(255,149,0)" + return attentionColorString + + +def get_macro_version_from_file(filename: str) -> str: + re_version = re.compile(r"^__Version__\s*=\s*(['\"])(.*)\1", flags=re.IGNORECASE) + with open(filename, "r", errors="ignore") as f: + line_counter = 0 + max_lines_to_scan = 50 + while line_counter < max_lines_to_scan: + line_counter += 1 + line = f.readline() + if line.startswith("__"): + match = re.match(re_version, line) + if match: + return match.group(2) + return "" + + +def update_macro_installation_details(repo) -> None: + if repo is None or not hasattr(repo, "macro") or repo.macro is None: + FreeCAD.Console.PrintLog(f"Requested macro details for non-macro object\n") + return + test_file_one = os.path.join(FreeCAD.getUserMacroDir(True), repo.macro.filename) + test_file_two = os.path.join( + FreeCAD.getUserMacroDir(True), "Macro_" + repo.macro.filename + ) + if os.path.exists(test_file_one): + repo.updated_timestamp = os.path.getmtime(test_file_one) + repo.installed_version = get_macro_version_from_file(test_file_one) + elif os.path.exists(test_file_two): + repo.updated_timestamp = os.path.getmtime(test_file_two) + repo.installed_version = get_macro_version_from_file(test_file_two) + else: + return + + # @} diff --git a/src/Mod/AddonManager/addonmanager_workers.py b/src/Mod/AddonManager/addonmanager_workers.py index a75d43b355..45dd516e12 100644 --- a/src/Mod/AddonManager/addonmanager_workers.py +++ b/src/Mod/AddonManager/addonmanager_workers.py @@ -299,7 +299,9 @@ class LoadMacrosFromCacheWorker(QtCore.QThread): if QtCore.QThread.currentThread().isInterruptionRequested(): return new_macro = Macro.from_cache(item) - self.add_macro_signal.emit(AddonManagerRepo.from_macro(new_macro)) + repo = AddonManagerRepo.from_macro(new_macro) + utils.update_macro_installation_details(repo) + self.add_macro_signal.emit(repo) class CheckWorkbenchesForUpdatesWorker(QtCore.QThread): @@ -588,6 +590,7 @@ class FillMacroListWorker(QtCore.QThread): macro.src_filename = os.path.join(dirpath, filename) repo = AddonManagerRepo.from_macro(macro) repo.url = "https://github.com/FreeCAD/FreeCAD-macros.git" + utils.update_macro_installation_details(repo) self.add_macro_signal.emit(repo) def retrieve_macros_from_wiki(self): @@ -633,6 +636,7 @@ class FillMacroListWorker(QtCore.QThread): macro.on_wiki = True repo = AddonManagerRepo.from_macro(macro) repo.url = "https://wiki.freecad.org/Macros_recipes" + utils.update_macro_installation_details(repo) self.add_macro_signal.emit(repo) @@ -1053,17 +1057,8 @@ class GetMacroDetailsWorker(QtCore.QThread): mac = mac.replace("+", "%2B") url = "https://wiki.freecad.org/Macro_" + mac self.macro.fill_details_from_wiki(url) - if self.macro.is_installed(): - already_installed_msg = ( - '' - + translate("AddonsInstaller", "This macro is already installed.") - + "
" - ) - else: - already_installed_msg = "" message = ( - already_installed_msg - + "

" + "

" + self.macro.name + "

" + self.macro.desc @@ -1719,6 +1714,7 @@ class UpdateSingleWorker(QtCore.QThread): install_succeeded, errors = repo.macro.install( FreeCAD.getUserMacroDir(True) ) + utils.update_macro_installation_details(repo) if install_succeeded: self.success.emit(repo) diff --git a/src/Mod/AddonManager/package_details.py b/src/Mod/AddonManager/package_details.py index 2e4a115def..508073d7d4 100644 --- a/src/Mod/AddonManager/package_details.py +++ b/src/Mod/AddonManager/package_details.py @@ -31,12 +31,14 @@ from datetime import date, timedelta import FreeCAD -from addonmanager_utilities import translate # this needs to be as is for pylupdate +import addonmanager_utilities as utils from addonmanager_workers import ShowWorker, GetMacroDetailsWorker from AddonManagerRepo import AddonManagerRepo import inspect +translate = FreeCAD.Qt.translate + class PackageDetails(QWidget): @@ -93,31 +95,34 @@ class PackageDetails(QWidget): self.ui.buttonExecute.hide() if repo.update_status != AddonManagerRepo.UpdateStatus.NOT_INSTALLED: - installed_version_string = "" - if repo.installed_version: - installed_version_string = translate("AddonsInstaller", "Version") + " " - installed_version_string += repo.installed_version - else: - installed_version_string = ( - translate( - "AddonsInstaller", "Unknown version (no package.xml file found)" - ) - + " " - ) + version = repo.installed_version + date = "" + installed_version_string = "

" if repo.updated_timestamp: - installed_version_string += ( - " " + translate("AddonsInstaller", "installed on") + " " - ) - installed_version_string += ( + date = ( QDateTime.fromTime_t(repo.updated_timestamp) .date() .toString(Qt.SystemLocaleShortDate) ) - installed_version_string += ". " + if version and date: + installed_version_string += ( + translate( + "AddonsInstaller", f"Version {version} installed on {date}" + ) + + ". " + ) + elif version: + installed_version_string += ( + translate("AddonsInstaller", f"Version {version} installed") + ". " + ) + elif date: + installed_version_string += ( + translate("AddonsInstaller", f"Installed on {date}") + ". " + ) else: installed_version_string += ( - translate("AddonsInstaller", "installed") + ". " + translate("AddonsInstaller", "Installed") + ". " ) if repo.update_status == AddonManagerRepo.UpdateStatus.UPDATE_AVAILABLE: @@ -129,12 +134,20 @@ class PackageDetails(QWidget): ) installed_version_string += repo.metadata.Version installed_version_string += "." + elif repo.macro and repo.macro.version: + installed_version_string += ( + "" + + translate("AddonsInstaller", "Update available to version") + + " " + ) + installed_version_string += repo.macro.version + installed_version_string += "." else: installed_version_string += ( "" + translate( "AddonsInstaller", - "Update available to unknown version (no package.xml file found)", + "An update is available", ) + "." ) @@ -166,19 +179,32 @@ class PackageDetails(QWidget): + "." ) - basedir = FreeCAD.getUserAppDataDir() - moddir = os.path.join(basedir, "Mod", repo.name) - installed_version_string += ( - "
" - + translate("AddonsInstaller", "Installation location") - + ": " - + moddir + installed_version_string += "

" + self.ui.labelPackageDetails.setText(installed_version_string) + if repo.update_status == AddonManagerRepo.UpdateStatus.UPDATE_AVAILABLE: + self.ui.labelPackageDetails.setStyleSheet( + "color:" + utils.attention_color_string() + ) + else: + self.ui.labelPackageDetails.setStyleSheet( + "color:" + utils.bright_color_string() + ) + self.ui.labelPackageDetails.show() + + if repo.macro is not None: + moddir = FreeCAD.getUserMacroDir(True) + else: + basedir = FreeCAD.getUserAppDataDir() + moddir = os.path.join(basedir, "Mod", repo.name) + installationLocationString = ( + translate("AddonsInstaller", "Installation location") + ": " + moddir ) - self.ui.labelPackageDetails.setText(installed_version_string) - self.ui.labelPackageDetails.show() + self.ui.labelInstallationLocation.setText(installationLocationString) + self.ui.labelInstallationLocation.show() else: self.ui.labelPackageDetails.hide() + self.ui.labelInstallationLocation.hide() if repo.update_status == AddonManagerRepo.UpdateStatus.NOT_INSTALLED: self.ui.buttonInstall.show() @@ -206,14 +232,6 @@ class PackageDetails(QWidget): self.ui.buttonUpdate.hide() self.ui.buttonCheckForUpdate.hide() - warningColorString = "rgb(255,0,0)" - if hasattr(QApplication.instance(), "styleSheet"): - # Qt 5.9 doesn't give a QApplication instance, so can't give the stylesheet info - if "dark" in QApplication.instance().styleSheet().lower(): - warningColorString = "rgb(255,50,50)" - else: - warningColorString = "rgb(200,0,0)" - if repo.obsolete: self.ui.labelWarningInfo.show() self.ui.labelWarningInfo.setText( @@ -221,7 +239,9 @@ class PackageDetails(QWidget): + translate("AddonsInstaller", "WARNING: This addon is obsolete") + "" ) - self.ui.labelWarningInfo.setStyleSheet("color:" + warningColorString) + self.ui.labelWarningInfo.setStyleSheet( + "color:" + utils.warning_color_string() + ) elif repo.python2: self.ui.labelWarningInfo.show() self.ui.labelWarningInfo.setText( @@ -229,7 +249,9 @@ class PackageDetails(QWidget): + translate("AddonsInstaller", "WARNING: This addon is Python 2 Only") + "" ) - self.ui.labelWarningInfo.setStyleSheet("color:" + warningColorString) + self.ui.labelWarningInfo.setStyleSheet( + "color:" + utils.warning_color_string() + ) else: self.ui.labelWarningInfo.hide() @@ -416,6 +438,12 @@ class Ui_PackageDetails(object): self.verticalLayout_2.addWidget(self.labelPackageDetails) + self.labelInstallationLocation = QLabel(PackageDetails) + self.labelInstallationLocation.setTextInteractionFlags(Qt.TextSelectableByMouse) + self.labelInstallationLocation.hide() + + self.verticalLayout_2.addWidget(self.labelInstallationLocation) + self.labelWarningInfo = QLabel(PackageDetails) self.labelWarningInfo.hide()