diff --git a/EDMarketConnector.py b/EDMarketConnector.py index 56fc0f63..92c0f491 100755 --- a/EDMarketConnector.py +++ b/EDMarketConnector.py @@ -2228,7 +2228,7 @@ sys.path: {sys.path}''' logger.exception(f"EDMC Critical Error: {err}") title = tr.tl("Error") # LANG: Generic error prefix message = tr.tl( # LANG: EDMC Critical Error Notification - "EDSM encountered a critical error, and cannot recover. EDMC is shutting down for its own protection!" + "EDMC encountered a critical error, and cannot recover. EDMC is shutting down for its own protection!" ) err = f"{err.__class__.__name__}: {err}" # type: ignore # hijacking the existing exception detection detail = tr.tl( # LANG: EDMC Critical Error Details diff --git a/L10n/en.template b/L10n/en.template index 242592ed..b45dfed9 100644 --- a/L10n/en.template +++ b/L10n/en.template @@ -37,7 +37,7 @@ "Error: unable to get token" = "Error: unable to get token"; /* EDMarketConnector.py: EDMC Critical Error Notification; */ -"EDSM encountered a critical error, and cannot recover. EDMC is shutting down for its own protection!" = "EDSM encountered a critical error, and cannot recover. EDMC is shutting down for its own protection!"; +"EDMC encountered a critical error, and cannot recover. EDMC is shutting down for its own protection!" = "EDMC encountered a critical error, and cannot recover. EDMC is shutting down for its own protection!"; /* EDMarketConnector.py: EDMC Critical Error Details; */ "Here's what EDMC Detected:\r\n\r\n{ERR}\r\n\r\nDo you want to file a Bug Report on GitHub?" = "Here's what EDMC Detected:\r\n\r\n{ERR}\r\n\r\nDo you want to file a Bug Report on GitHub?"; @@ -126,6 +126,15 @@ /* EDMarketConnector.py: Label for 'Copy' as in 'Copy and Paste'; ttkHyperlinkLabel.py: Label for 'Copy' as in 'Copy and Paste'; In files: EDMarketConnector.py:962; ttkHyperlinkLabel.py:53; */ "Copy" = "Copy"; +/* myNotebook.py: Label for 'Cut' as in 'Cut and Paste'; */ +"Cut" = "Cut"; + +/* myNotebook.py: Label for 'Paste' as in 'Copy and Paste'; */ +"Paste" = "Paste"; + +/* myNotebook.py: Label for 'Select All'; */ +"Select All" = "Select All"; + /* EDMarketConnector.py: CAPI auth aborted because of killswitch; EDMarketConnector.py: CAPI auth query aborted because of killswitch; In files: EDMarketConnector.py:973; EDMarketConnector.py:1067; */ "CAPI auth disabled by killswitch" = "CAPI auth disabled by killswitch"; diff --git a/config/__init__.py b/config/__init__.py index fd81ea19..1a0ae24a 100644 --- a/config/__init__.py +++ b/config/__init__.py @@ -54,7 +54,7 @@ appcmdname = 'EDMC' # # Major.Minor.Patch(-prerelease)(+buildmetadata) # NB: Do *not* import this, use the functions appversion() and appversion_nobuild() -_static_appversion = '5.10.6' +_static_appversion = '5.11.0-alpha3' _cached_version: semantic_version.Version | None = None copyright = '© 2015-2019 Jonathan Harris, 2020-2024 EDCD' diff --git a/myNotebook.py b/myNotebook.py index 8a3cf901..635af5c1 100644 --- a/myNotebook.py +++ b/myNotebook.py @@ -67,12 +67,14 @@ class EntryMenu(ttk.Entry): ttk.Entry.__init__(self, *args, **kwargs) self.menu = tk.Menu(self, tearoff=False) - self.menu.add_command(label="Copy", command=self.copy) - self.menu.add_command(label="Cut", command=self.cut) + self.menu.add_command(label=tr.tl("Copy"), command=self.copy) # LANG: Label for 'Copy' as in 'Copy and Paste' + self.menu.add_command(label=tr.tl("Cut"), command=self.cut) # LANG: Label for 'Cut' as in 'Cut and Paste' self.menu.add_separator() - self.menu.add_command(label="Paste", command=self.paste) + # LANG: Label for 'Paste' as in 'Copy and Paste' + self.menu.add_command(label=tr.tl("Paste"), command=self.paste) self.menu.add_separator() - self.menu.add_command(label="Select All", command=self.select_all) + # LANG: Label for 'Select All' + self.menu.add_command(label=tr.tl("Select All"), command=self.select_all) self.bind("", self.display_popup) diff --git a/plug.py b/plug.py index dad08bc6..bd98f2bb 100644 --- a/plug.py +++ b/plug.py @@ -47,14 +47,14 @@ last_error = LastError() class Plugin: """An EDMC plugin.""" - def __init__(self, name: str, loadfile: str | None, plugin_logger: logging.Logger | None): + def __init__(self, name: str, loadfile: str | None, plugin_logger: logging.Logger | None): # noqa: CCR001 """ Load a single plugin. :param name: Base name of the file being loaded from. :param loadfile: Full path/filename of the plugin. :param plugin_logger: The logging instance for this plugin to use. - :raises Exception: Typically ImportError or OSError + :raises Exception: Typically, ImportError or OSError """ self.name: str = name # Display name. self.folder: str | None = name # basename of plugin folder. None for internal plugins. @@ -66,19 +66,23 @@ class Plugin: try: filename = 'plugin_' filename += name.encode(encoding='ascii', errors='replace').decode('utf-8').replace('.', '_') - module = importlib.machinery.SourceFileLoader( - filename, - loadfile - ).load_module() - if getattr(module, 'plugin_start3', None): - newname = module.plugin_start3(os.path.dirname(loadfile)) - self.name = str(newname) if newname else self.name - self.module = module - elif getattr(module, 'plugin_start', None): - logger.warning(f'plugin {name} needs migrating\n') - PLUGINS_not_py3.append(self) + spec = importlib.util.spec_from_file_location(filename, loadfile) + # Replaces older load_module() code. Includes a safety check that the module name is set. + if spec is not None and spec.loader is not None: + module = importlib.util.module_from_spec(spec) + sys.modules[module.__name__] = module + spec.loader.exec_module(module) + if getattr(module, 'plugin_start3', None): + newname = module.plugin_start3(os.path.dirname(loadfile)) + self.name = str(newname) if newname else self.name + self.module = module + elif getattr(module, 'plugin_start', None): + logger.warning(f'plugin {name} needs migrating\n') + PLUGINS_not_py3.append(self) + else: + logger.error(f'plugin {name} has no plugin_start3() function') else: - logger.error(f'plugin {name} has no plugin_start3() function') + logger.error(f'Failed to load Plugin "{name}" from file "{loadfile}"') except Exception: logger.exception(f': Failed for Plugin "{name}"') raise diff --git a/ttkHyperlinkLabel.py b/ttkHyperlinkLabel.py index 3ec0a26d..6266d9fb 100644 --- a/ttkHyperlinkLabel.py +++ b/ttkHyperlinkLabel.py @@ -70,10 +70,6 @@ class HyperlinkLabel(tk.Label or ttk.Label): # type: ignore ttk.Label.__init__(self, master, **kw) self.bind('', self._click) - - self.menu = tk.Menu(tearoff=tk.FALSE) - # LANG: Label for 'Copy' as in 'Copy and Paste' - self.menu.add_command(label=tr.tl('Copy'), command=self.copy) # As in Copy and Paste self.bind('', self._contextmenu) self.bind('', self._enter) @@ -87,29 +83,6 @@ class HyperlinkLabel(tk.Label or ttk.Label): # type: ignore # Add Menu Options self.plug_options = kw.pop('plug_options', None) self.name = kw.get('name', None) - if self.name == 'ship': - self.menu.add_separator() - for url in plug.provides('shipyard_url'): - self.menu.add_command( - label=tr.tl("Open in {URL}").format(URL=url), # LANG: Open Element In Selected Provider - command=partial(self.open_shipyard, url) - ) - - if self.name == 'station': - self.menu.add_separator() - for url in plug.provides('station_url'): - self.menu.add_command( - label=tr.tl("Open in {URL}").format(URL=url), # LANG: Open Element In Selected Provider - command=partial(self.open_station, url) - ) - - if self.name == 'system': - self.menu.add_separator() - for url in plug.provides('system_url'): - self.menu.add_command( - label=tr.tl("Open in {URL}").format(URL=url), # LANG: Open Element In Selected Provider - command=partial(self.open_system, url) - ) def open_shipyard(self, url: str): """Open the Current Ship Loadout in the Selected Provider.""" @@ -122,14 +95,13 @@ class HyperlinkLabel(tk.Label or ttk.Label): # type: ignore return webbrowser.open(opener) else: # Avoid file length limits if possible - provider = config.get_str('shipyard_provider', default='EDSY') - target = plug.invoke(provider, 'EDSY', 'shipyard_url', loadout, monitor.is_beta) + target = plug.invoke(url, 'EDSY', 'shipyard_url', loadout, monitor.is_beta) file_name = path.join(config.app_dir_path, "last_shipyard.html") with open(file_name, 'w') as f: f.write(SHIPYARD_HTML_TEMPLATE.format( link=html.escape(str(target)), - provider_name=html.escape(str(provider)), + provider_name=html.escape(str(url)), ship_name=html.escape("Ship") )) @@ -212,8 +184,41 @@ class HyperlinkLabel(tk.Label or ttk.Label): # type: ignore webbrowser.open(url) def _contextmenu(self, event: tk.Event) -> None: + """ + Display the context menu when right-clicked. + + :param event: The event object. + """ + menu = tk.Menu(tearoff=tk.FALSE) + # LANG: Label for 'Copy' as in 'Copy and Paste' + menu.add_command(label=tr.tl('Copy'), command=self.copy) # As in Copy and Paste + + if self.name == 'ship': + menu.add_separator() + for url in plug.provides('shipyard_url'): + menu.add_command( + label=tr.tl("Open in {URL}").format(URL=url), # LANG: Open Element In Selected Provider + command=partial(self.open_shipyard, url) + ) + + if self.name == 'station': + menu.add_separator() + for url in plug.provides('station_url'): + menu.add_command( + label=tr.tl("Open in {URL}").format(URL=url), # LANG: Open Element In Selected Provider + command=partial(self.open_station, url) + ) + + if self.name == 'system': + menu.add_separator() + for url in plug.provides('system_url'): + menu.add_command( + label=tr.tl("Open in {URL}").format(URL=url), # LANG: Open Element In Selected Provider + command=partial(self.open_system, url) + ) + if self['text'] and (self.popup_copy(self['text']) if callable(self.popup_copy) else self.popup_copy): - self.menu.post(event.x_root, event.y_root) + menu.post(event.x_root, event.y_root) def copy(self) -> None: """Copy the current text to the clipboard."""