""" common_coreutils.py - Common Plugin Functions. Copyright (c) EDCD, All Rights Reserved Licensed under the GNU General Public License. See LICENSE file. This is an EDMC 'core' plugin. All EDMC plugins are *dynamically* loaded at run-time. We build for Windows using `py2exe`. `py2exe` can't possibly know about anything in the dynamically loaded core plugins. Thus, you **MUST** check if any imports you add in this file are only referenced in this file (or only in any other core plugin), and if so... YOU MUST ENSURE THAT PERTINENT ADJUSTMENTS ARE MADE IN `build.py` TO ENSURE THE FILES ARE ACTUALLY PRESENT IN AN END-USER INSTALLATION ON WINDOWS. """ # pylint: disable=import-error from __future__ import annotations from typing import Any, cast from collections.abc import Mapping import tkinter as tk import base64 import gzip import io import json import os import re import myNotebook as nb # noqa: N813 from EDMCLogging import get_main_logger from companion import CAPIData from l10n import translations as tr logger = get_main_logger() if not os.getenv('EDMC_NO_UI'): # Functions using show_password_var MUST have TK set up show_password_var = tk.BooleanVar() # Global Padding Preferences PADX = 10 BUTTONX = 12 # indent Checkbuttons and Radiobuttons PADY = 1 # close spacing BOXY = 2 # box spacing SEPY = 10 # seperator line spacing STATION_UNDOCKED = '×' # "Station" name to display when not docked = U+00D7 # Known colonisation ship names match one of the following: # "$EXT_PANEL_ColonisationShip;" # "$EXT_PANEL_ColonisationShip; " with "" being eg; "Okawara Horizons" # "$EXT_PANEL_ColonisationShip:#index=;" # "$EXT_PANEL_ColonisationShip:#index=; " _RE_COLONISATION_SHIP = re.compile(r'\$EXT_PANEL_ColonisationShip(?::#index=(\d+))?;(?: (.+))?') def localised_name(value: str | None) -> str | None: """ Handle localisation of strings in places like station names. This is currently an issue for system colonisation ships that have a station name along the lines of "$EXT_PANEL_ColonisationShip; Okawara Horizons" when written to the journal, but would be better displayed as "System Colonisation Ship Okawara Horizona" (or other appropriately localised equivalent). If no (known) localisable strings are present, the input will be returned unmodified. :param value: The non-localised string. :return: The localised string, or None if the input was None. """ if value is None: return None colonisation_ship_match = _RE_COLONISATION_SHIP.match(value) if colonisation_ship_match: ship_index = colonisation_ship_match.group(1) ship_name = colonisation_ship_match.group(2) # Use the ship's index as its name if no name was provided. ship_id = ship_name or ship_index if ship_id: # LANG: Colonisation ship name, with title value = tr.tl("System Colonisation Ship {NAME}").format(NAME=ship_id) else: # LANG: Unidentified colonisation ship value = tr.tl("System Colonisation Ship") return value def plugin_start3(plugin_dir: str) -> str: """ Start the plugin. :param plugin_dir: NAme of directory this was loaded from. :return: Identifier string for this plugin. """ return 'CommonCoreUtils' def api_keys_label_common(this, cur_row: int, frame: nb.Frame): """ Prepare the box for API Key Loading. This is an EDMC Common Function. :param this: The module global from the calling module. :param cur_row: The current row in the calling module's config page. :param frame: The current frame in the calling module's config page. :return: The updated module global from the calling module. """ # LANG: EDSM API key label this.apikey_label = nb.Label(frame, text=tr.tl('API Key')) this.apikey_label.grid(row=cur_row, padx=PADX, pady=PADY, sticky=tk.W) this.apikey = nb.EntryMenu(frame, show="*", width=50) this.apikey.grid(row=cur_row, column=1, padx=PADX, pady=BOXY, sticky=tk.EW) return this def show_pwd_var_common(frame: nb.Frame, cur_row: int, this): """ Allow unmasking of the API Key. This is an EDMC Common Function. :param cur_row: The current row in the calling module's config page. :param frame: The current frame in the calling module's config page. """ show_password_var.set(False) # Password is initially masked show_password_checkbox = nb.Checkbutton( frame, text=tr.tl('Show API Key'), # LANG: Text EDSM Show API Key variable=show_password_var, command=lambda: toggle_password_visibility_common(this) ) show_password_checkbox.grid(row=cur_row, columnspan=2, padx=BUTTONX, pady=PADY, sticky=tk.W) # Return a URL for the current ship def shipyard_url_common(loadout: Mapping[str, Any]) -> str: """ Construct a URL for ship loadout. This is an EDMC Common Function. :param loadout: The ship loadout data. :return: The constructed URL for the ship loadout. """ # Convert loadout to JSON and gzip compress it string = json.dumps(loadout, ensure_ascii=False, sort_keys=True, separators=(',', ':')).encode('utf-8') if not string: return '' out = io.BytesIO() with gzip.GzipFile(fileobj=out, mode='w') as f: f.write(string) encoded_data = base64.urlsafe_b64encode(out.getvalue()).decode().replace('=', '%3D') return encoded_data def station_link_common(data: CAPIData, this): """ Set the Staion Name. This is an EDMC Common Function. :param data: A CAPI Data Entry. :param this: The module global from the calling module. """ if data['commander']['docked'] or this.on_foot and this.station_name: this.station_link['text'] = localised_name(this.station_name) elif data['lastStarport']['name'] and data['lastStarport']['name'] != "": this.station_link['text'] = STATION_UNDOCKED else: this.station_link['text'] = '' def this_format_common(this, state: Mapping[str, Any]): """ Gather Common 'This' Elements. This is an EDMC Common Function. :param this: The module global from the calling module. :param state: `monitor.state`. """ this.system_address = state['SystemAddress'] this.system_name = state['SystemName'] this.system_population = state['SystemPopulation'] this.station_name = state['StationName'] this.station_marketid = state['MarketID'] this.station_type = state['StationType'] this.on_foot = state['OnFoot'] def toggle_password_visibility_common(this): """ Toggle if the API Key is visible or not. This is an EDMC Common Function. :param this: The module global from the calling module. """ if show_password_var.get(): this.apikey.config(show="") # type: ignore else: this.apikey.config(show="*") # type: ignore def station_name_setter_common(this): """ Set the Station Name. This is an EDMC Common Function. :param this: The module global from the calling module. """ to_set: str = cast(str, this.station_name) if not to_set: if this.system_population is not None and this.system_population > 0: to_set = STATION_UNDOCKED else: to_set = '' to_set = str(localised_name(to_set)) this.station_link['text'] = to_set def cmdr_data_initial_common(this, data: CAPIData): """ Set the common CMDR Data. This is an EDMC Common Function. :param this: The module global from the calling module. :param data: The latest merged CAPI data. """ # Always store initially, even if we're not the *current* system provider. if not this.station_marketid and data['commander']['docked']: this.station_marketid = data['lastStarport']['id'] # Only trust CAPI if these aren't yet set if not this.system_name: this.system_name = data['lastSystem']['name'] if not this.station_name and data['commander']['docked']: this.station_name = data['lastStarport']['name']