mirror of
https://github.com/EDCD/EDMarketConnector.git
synced 2025-04-17 01:22:19 +03:00
Merge branch '2.2'
This commit is contained in:
commit
52da973265
@ -70,6 +70,9 @@
|
||||
/* Combat rank. [stats.py] */
|
||||
"Competent" = "Competent";
|
||||
|
||||
/* Tab heading in settings. [prefs.py] */
|
||||
"Configuration" = "Configuration";
|
||||
|
||||
/* Update button in main window. [EDMarketConnector.py] */
|
||||
"cooldown {SS}s" = "cooldown {SS}s";
|
||||
|
||||
@ -103,6 +106,9 @@
|
||||
/* Empire rank. [stats.py] */
|
||||
"Duke" = "Duke";
|
||||
|
||||
/* Configuration setting. [prefs.py] */
|
||||
"E:D log file location" = "E:D log file location";
|
||||
|
||||
/* Empire rank. [stats.py] */
|
||||
"Earl" = "Earl";
|
||||
|
||||
@ -181,7 +187,7 @@
|
||||
/* Dark theme color setting. [prefs.py] */
|
||||
"Highlighted text" = "Highlighted text";
|
||||
|
||||
/* Tab heading in settings on Windows. [prefs.py] */
|
||||
/* Hotkey/Shortcut settings prompt on Windows. [prefs.py] */
|
||||
"Hotkey" = "Hotkey";
|
||||
|
||||
/* [prefs.py] */
|
||||
@ -190,7 +196,7 @@
|
||||
/* Tab heading in settings. [prefs.py] */
|
||||
"Identity" = "Identity";
|
||||
|
||||
/* Tab heading in settings on OSX. [prefs.py] */
|
||||
/* Hotkey/Shortcut settings prompt on OSX. [prefs.py] */
|
||||
"Keyboard shortcut" = "Keyboard shortcut";
|
||||
|
||||
/* Empire rank. [stats.py] */
|
||||
|
63
config.py
63
config.py
@ -33,6 +33,7 @@ elif platform=='win32':
|
||||
REG_OPENED_EXISTING_KEY = 0x00000002
|
||||
REG_SZ = 1
|
||||
REG_DWORD = 4
|
||||
REG_MULTI_SZ = 7
|
||||
|
||||
RegCreateKeyEx = ctypes.windll.advapi32.RegCreateKeyExW
|
||||
RegCreateKeyEx.restype = LONG
|
||||
@ -62,6 +63,10 @@ elif platform=='win32':
|
||||
RegDeleteKey.restype = LONG
|
||||
RegDeleteKey.argtypes = [HKEY, LPCWSTR]
|
||||
|
||||
RegDeleteValue = ctypes.windll.advapi32.RegDeleteValueW
|
||||
RegDeleteValue.restype = LONG
|
||||
RegDeleteValue.argtypes = [HKEY, LPCWSTR]
|
||||
|
||||
elif platform=='linux2':
|
||||
import codecs
|
||||
# requires python-iniparse package - ConfigParser that ships with Python < 3.2 doesn't support unicode
|
||||
@ -101,17 +106,21 @@ class Config:
|
||||
# Don't use Python's settings if interactive
|
||||
self.bundle = 'uk.org.marginal.%s' % appname.lower()
|
||||
NSBundle.mainBundle().infoDictionary()['CFBundleIdentifier'] = self.bundle
|
||||
self.bundle = NSBundle.mainBundle().bundleIdentifier()
|
||||
else:
|
||||
self.bundle = NSBundle.mainBundle().bundleIdentifier()
|
||||
self.defaults = NSUserDefaults.standardUserDefaults()
|
||||
settings = self.defaults.persistentDomainForName_(self.bundle) or {}
|
||||
self.settings = dict(settings)
|
||||
self.settings = dict(self.defaults.persistentDomainForName_(self.bundle) or {}) # make writeable
|
||||
|
||||
# Check out_dir exists
|
||||
if not self.get('outdir') or not isdir(self.get('outdir')):
|
||||
self.set('outdir', NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, True)[0])
|
||||
|
||||
def get(self, key):
|
||||
return self.settings.get(key)
|
||||
val = self.settings.get(key)
|
||||
if hasattr(val, '__iter__'):
|
||||
return list(val) # make writeable
|
||||
else:
|
||||
return val
|
||||
|
||||
def getint(self, key):
|
||||
try:
|
||||
@ -122,6 +131,9 @@ class Config:
|
||||
def set(self, key, val):
|
||||
self.settings[key] = val
|
||||
|
||||
def delete(self, key):
|
||||
self.settings.pop(key, None)
|
||||
|
||||
def save(self):
|
||||
self.defaults.setPersistentDomain_forName_(self.settings, self.bundle)
|
||||
self.defaults.synchronize()
|
||||
@ -182,11 +194,13 @@ class Config:
|
||||
def get(self, key):
|
||||
typ = DWORD()
|
||||
size = DWORD()
|
||||
if RegQueryValueEx(self.hkey, key, 0, ctypes.byref(typ), None, ctypes.byref(size)) or typ.value != REG_SZ:
|
||||
if RegQueryValueEx(self.hkey, key, 0, ctypes.byref(typ), None, ctypes.byref(size)) or typ.value not in [REG_SZ, REG_MULTI_SZ]:
|
||||
return None
|
||||
buf = ctypes.create_unicode_buffer(size.value / 2)
|
||||
if RegQueryValueEx(self.hkey, key, 0, ctypes.byref(typ), buf, ctypes.byref(size)):
|
||||
return None
|
||||
elif typ.value == REG_MULTI_SZ:
|
||||
return [x.strip() for x in ctypes.wstring_at(buf, len(buf)-2).split(u'\x00')]
|
||||
else:
|
||||
return buf.value
|
||||
|
||||
@ -205,9 +219,16 @@ class Config:
|
||||
RegSetValueEx(self.hkey, key, 0, REG_SZ, buf, len(buf)*2)
|
||||
elif isinstance(val, numbers.Integral):
|
||||
RegSetValueEx(self.hkey, key, 0, REG_DWORD, ctypes.byref(DWORD(val)), 4)
|
||||
elif hasattr(val, '__iter__'): # iterable
|
||||
stringval = u'\x00'.join([unicode(x) or u' ' for x in val] + [u'']) # null terminated non-empty strings
|
||||
buf = ctypes.create_unicode_buffer(stringval)
|
||||
RegSetValueEx(self.hkey, key, 0, REG_MULTI_SZ, buf, len(buf)*2)
|
||||
else:
|
||||
raise NotImplementedError()
|
||||
|
||||
def delete(self, key):
|
||||
RegDeleteValue(self.hkey, key)
|
||||
|
||||
def save(self):
|
||||
pass # Redundant since registry keys are written immediately
|
||||
|
||||
@ -217,6 +238,8 @@ class Config:
|
||||
|
||||
elif platform=='linux2':
|
||||
|
||||
SECTION = 'config'
|
||||
|
||||
def __init__(self):
|
||||
|
||||
# http://standards.freedesktop.org/basedir-spec/latest/ar01s03.html
|
||||
@ -240,31 +263,41 @@ class Config:
|
||||
try:
|
||||
self.config.readfp(codecs.open(self.filename, 'r', 'utf-8'))
|
||||
except:
|
||||
self.config.add_section('config')
|
||||
self.config.add_section(self.SECTION)
|
||||
|
||||
if not self.get('outdir') or not isdir(self.get('outdir')):
|
||||
self.set('outdir', expanduser('~'))
|
||||
|
||||
def set(self, key, val):
|
||||
assert isinstance(val, basestring) or isinstance(val, numbers.Integral), type(val)
|
||||
self.config.set('config', key, val)
|
||||
|
||||
def get(self, key):
|
||||
try:
|
||||
return self.config.get('config', key) # all values are stored as strings
|
||||
val = self.config.get(self.SECTION, key)
|
||||
if u'\n' in val:
|
||||
return val.split(u'\n')
|
||||
else:
|
||||
return val
|
||||
except:
|
||||
return None
|
||||
|
||||
def getint(self, key):
|
||||
try:
|
||||
return int(self.config.get('config', key)) # all values are stored as strings
|
||||
return self.config.getint(self.SECTION, key)
|
||||
except:
|
||||
return 0
|
||||
|
||||
def set(self, key, val):
|
||||
if isinstance(val, basestring) or isinstance(val, numbers.Integral):
|
||||
self.config.set(self.SECTION, key, val)
|
||||
elif hasattr(val, '__iter__'): # iterable
|
||||
self.config.set(self.SECTION, key, u'\n'.join([unicode(x) for x in val]))
|
||||
else:
|
||||
raise NotImplementedError()
|
||||
|
||||
def delete(self, key):
|
||||
self.config.remove_option(self.SECTION, key)
|
||||
|
||||
def save(self):
|
||||
h = codecs.open(self.filename, 'w', 'utf-8')
|
||||
h.write(unicode(self.config.data))
|
||||
h.close()
|
||||
with codecs.open(self.filename, 'w', 'utf-8') as h:
|
||||
h.write(unicode(self.config.data))
|
||||
|
||||
def close(self):
|
||||
self.save()
|
||||
|
@ -410,6 +410,11 @@ else: # Linux
|
||||
def unregister(self):
|
||||
pass
|
||||
|
||||
def play_good(self):
|
||||
pass
|
||||
|
||||
def play_bad(self):
|
||||
pass
|
||||
|
||||
# singleton
|
||||
hotkeymgr = HotkeyMgr()
|
||||
|
85
monitor.py
85
monitor.py
@ -1,7 +1,7 @@
|
||||
import atexit
|
||||
import re
|
||||
import threading
|
||||
from os import listdir, pardir, rename, unlink
|
||||
from os import listdir, pardir, rename, unlink, SEEK_SET, SEEK_CUR, SEEK_END
|
||||
from os.path import basename, exists, isdir, isfile, join
|
||||
from platform import machine
|
||||
import sys
|
||||
@ -53,6 +53,7 @@ elif platform=='win32':
|
||||
RegEnumKeyEx.argtypes = [HKEY, DWORD, LPWSTR, ctypes.POINTER(DWORD), ctypes.POINTER(DWORD), LPWSTR, ctypes.POINTER(DWORD), ctypes.POINTER(FILETIME)]
|
||||
|
||||
else:
|
||||
# Linux's inotify doesn't work over CIFS or NFS, so poll
|
||||
FileSystemEventHandler = object # dummy
|
||||
|
||||
|
||||
@ -63,9 +64,11 @@ class EDLogs(FileSystemEventHandler):
|
||||
def __init__(self):
|
||||
FileSystemEventHandler.__init__(self) # futureproofing - not need for current version of watchdog
|
||||
self.root = None
|
||||
self.logdir = self._logdir()
|
||||
self.logdir = self._logdir() # E:D client's default Logs directory, or None if not found
|
||||
self.currentdir = None # The actual logdir that we're monitoring
|
||||
self.logfile = None
|
||||
self.observer = None
|
||||
self.observed = None
|
||||
self.thread = None
|
||||
self.callbacks = { 'Jump': None, 'Dock': None }
|
||||
self.last_event = None # for communicating the Jump event
|
||||
@ -76,40 +79,58 @@ class EDLogs(FileSystemEventHandler):
|
||||
|
||||
def start(self, root):
|
||||
self.root = root
|
||||
if not self.logdir:
|
||||
logdir = config.get('logdir') or self.logdir
|
||||
if not logdir or not isdir(logdir):
|
||||
self.stop()
|
||||
return False
|
||||
if self.running():
|
||||
return True
|
||||
|
||||
if self.currentdir and self.currentdir != logdir:
|
||||
self.stop()
|
||||
self.currentdir = logdir
|
||||
|
||||
self.root.bind_all('<<MonitorJump>>', self.jump) # user-generated
|
||||
self.root.bind_all('<<MonitorDock>>', self.dock) # user-generated
|
||||
|
||||
# Set up a watchog observer. This is low overhead so is left running irrespective of whether monitoring is desired.
|
||||
if not self.observer:
|
||||
if __debug__:
|
||||
print 'Monitoring "%s"' % self.logdir
|
||||
elif getattr(sys, 'frozen', False):
|
||||
sys.stderr.write('Monitoring "%s"\n' % self.logdir)
|
||||
sys.stderr.flush() # Required for line to show up immediately on Windows
|
||||
|
||||
# File system events are unreliable/non-existent over network drives on Linux.
|
||||
# We can't easily tell whether a path points to a network drive, so assume
|
||||
# any non-standard logdir might be on a network drive and poll instead.
|
||||
polling = bool(config.get('logdir')) and platform != 'win32'
|
||||
if not polling and not self.observer:
|
||||
self.observer = Observer()
|
||||
self.observer.daemon = True
|
||||
self.observer.schedule(self, self.logdir)
|
||||
self.observer.start()
|
||||
atexit.register(self.observer.stop)
|
||||
|
||||
# Latest pre-existing logfile - e.g. if E:D is already running. Assumes logs sort alphabetically.
|
||||
logfiles = sorted([x for x in listdir(self.logdir) if x.startswith('netLog.')])
|
||||
self.logfile = logfiles and join(self.logdir, logfiles[-1]) or None
|
||||
if not self.observed and not polling:
|
||||
self.observed = self.observer.schedule(self, self.currentdir)
|
||||
|
||||
# Latest pre-existing logfile - e.g. if E:D is already running. Assumes logs sort alphabetically.
|
||||
try:
|
||||
logfiles = sorted([x for x in listdir(logdir) if x.startswith('netLog.')])
|
||||
self.logfile = logfiles and join(logdir, logfiles[-1]) or None
|
||||
except:
|
||||
self.logfile = None
|
||||
|
||||
if __debug__:
|
||||
print '%s "%s"' % (polling and 'Polling' or 'Monitoring', logdir)
|
||||
print 'Start logfile "%s"' % self.logfile
|
||||
|
||||
if not self.running():
|
||||
self.thread = threading.Thread(target = self.worker, name = 'netLog worker')
|
||||
self.thread.daemon = True
|
||||
self.thread.start()
|
||||
|
||||
self.thread = threading.Thread(target = self.worker, name = 'netLog worker')
|
||||
self.thread.daemon = True
|
||||
self.thread.start()
|
||||
return True
|
||||
|
||||
def stop(self):
|
||||
self.thread = None # Orphan the worker thread
|
||||
if __debug__:
|
||||
print 'Stopping monitoring'
|
||||
self.currentdir = None
|
||||
if self.observed:
|
||||
self.observed = None
|
||||
self.observer.unschedule_all()
|
||||
self.thread = None # Orphan the worker thread - will terminate at next poll
|
||||
self.last_event = None
|
||||
|
||||
def running(self):
|
||||
@ -147,8 +168,8 @@ class EDLogs(FileSystemEventHandler):
|
||||
# Seek to the end of the latest log file
|
||||
logfile = self.logfile
|
||||
if logfile:
|
||||
loghandle = open(logfile, 'rt')
|
||||
loghandle.seek(0, 2) # seek to EOF
|
||||
loghandle = open(logfile, 'r')
|
||||
loghandle.seek(0, SEEK_END) # seek to EOF
|
||||
else:
|
||||
loghandle = None
|
||||
|
||||
@ -161,16 +182,30 @@ class EDLogs(FileSystemEventHandler):
|
||||
print "%s :\t%s %s" % ('Updated', docked and " docked" or "!docked", updated and " updated" or "!updated")
|
||||
|
||||
# Check whether new log file started, e.g. client (re)started.
|
||||
newlogfile = self.logfile
|
||||
if self.observed:
|
||||
newlogfile = self.logfile # updated by on_created watchdog callback
|
||||
else:
|
||||
# Poll
|
||||
logdir = config.get('logdir') or self.logdir
|
||||
try:
|
||||
logfiles = sorted([x for x in listdir(logdir) if x.startswith('netLog.')])
|
||||
newlogfile = logfiles and join(logdir, logfiles[-1]) or None
|
||||
except:
|
||||
if __debug__: print_exc()
|
||||
newlogfile = None
|
||||
|
||||
if logfile != newlogfile:
|
||||
logfile = newlogfile
|
||||
if loghandle:
|
||||
loghandle.close()
|
||||
loghandle = open(logfile, 'rt')
|
||||
if logfile:
|
||||
loghandle = open(logfile, 'r')
|
||||
if __debug__:
|
||||
print 'New logfile "%s"' % logfile
|
||||
|
||||
if logfile:
|
||||
system = visited = coordinates = None
|
||||
loghandle.seek(0, 1) # reset EOF flag
|
||||
loghandle.seek(0, SEEK_CUR) # reset EOF flag
|
||||
|
||||
for line in loghandle:
|
||||
match = regexp.match(line)
|
||||
|
@ -52,6 +52,7 @@ class Frame(platform == 'darwin' and tk.Frame or ttk.Frame):
|
||||
if platform == 'darwin':
|
||||
kw['background'] = kw.pop('background', PAGEBG)
|
||||
tk.Frame.__init__(self, master, **kw)
|
||||
tk.Frame(self).grid(pady=5)
|
||||
elif platform == 'win32':
|
||||
ttk.Frame.__init__(self, master, style='nb.TFrame', **kw)
|
||||
ttk.Frame(self).grid(pady=5) # top spacer
|
||||
@ -142,5 +143,7 @@ class OptionMenu(platform == 'darwin' and tk.OptionMenu or ttk.OptionMenu):
|
||||
elif platform == 'win32':
|
||||
# OptionMenu derives from Menubutton at the Python level, so uses Menubutton's style
|
||||
ttk.OptionMenu.__init__(self, master, variable, default, *values, style='nb.TMenubutton', **kw)
|
||||
self['menu'].configure(background = PAGEBG)
|
||||
else:
|
||||
ttk.OptionMenu.__init__(self, master, variable, default, *values, **kw)
|
||||
self['menu'].configure(background = ttk.Style().lookup('TMenu', 'background'))
|
||||
|
130
prefs.py
130
prefs.py
@ -132,7 +132,7 @@ class PreferencesDialog(tk.Toplevel):
|
||||
self.out_auto_button = nb.Checkbutton(outframe, text=_('Automatically update on docking'), variable=self.out_auto, command=self.outvarchanged) # Output setting
|
||||
self.out_auto_button.grid(columnspan=2, padx=BUTTONX, pady=(5,0), sticky=tk.W)
|
||||
|
||||
self.outdir_label = nb.Label(outframe, text=_('File location')) # Section heading in settings
|
||||
self.outdir_label = nb.Label(outframe, text=_('File location')+':') # Section heading in settings
|
||||
self.outdir_label.grid(padx=BUTTONX, pady=(5,0), sticky=tk.W)
|
||||
self.outdir = nb.Entry(outframe, takefocus=False)
|
||||
if config.get('outdir').startswith(config.home):
|
||||
@ -141,13 +141,30 @@ class PreferencesDialog(tk.Toplevel):
|
||||
self.outdir.insert(0, config.get('outdir'))
|
||||
self.outdir.grid(row=20, padx=(PADX,0), sticky=tk.EW)
|
||||
self.outbutton = nb.Button(outframe, text=(platform=='darwin' and _('Change...') or # Folder selection button on OSX
|
||||
_('Browse...')), command=self.outbrowse) # Folder selection button on Windows
|
||||
self.outbutton.grid(row=20, column=1, padx=PADX)
|
||||
_('Browse...')), # Folder selection button on Windows
|
||||
command = lambda:self.filebrowse(_('File location'), self.outdir))
|
||||
self.outbutton.grid(row=20, column=1, padx=PADX, sticky=tk.NSEW)
|
||||
nb.Frame(outframe).grid(pady=5) # bottom spacer
|
||||
|
||||
notebook.add(outframe, text=_('Output')) # Tab heading in settings
|
||||
|
||||
|
||||
# eddnframe = nb.Frame(notebook)
|
||||
# eddnframe.columnconfigure(0, weight=1)
|
||||
|
||||
# HyperlinkLabel(eddnframe, text='Elite Dangerous Data Network', background=nb.Label().cget('background'), url='https://github.com/jamesremuscat/EDDN/wiki', underline=True).grid(columnspan=2, padx=PADX, sticky=tk.W) # Don't translate
|
||||
# ttk.Separator(eddnframe, orient=tk.HORIZONTAL).grid(columnspan=2, padx=PADX, pady=PADY, sticky=tk.EW)
|
||||
# self.eddn_station= tk.IntVar(value = (output & config.OUT_MKT_EDDN) and 1)
|
||||
# nb.Checkbutton(eddnframe, text=_('Send station data to EDDN'), variable=self.eddn_station, command=self.outvarchanged).grid(columnspan=2, padx=BUTTONX, sticky=tk.W) # Output setting
|
||||
# self.eddn_auto_button = nb.Checkbutton(eddnframe, text=_('Automatically update on docking'), variable=self.out_auto, command=self.outvarchanged) # Output setting
|
||||
# self.eddn_auto_button.grid(columnspan=2, padx=BUTTONX, sticky=tk.W)
|
||||
# self.eddn_system= tk.IntVar(value = (output & config.OUT_SYS_EDDN) and 1)
|
||||
# nb.Checkbutton(eddnframe, text=_('Send system and scan data to EDDN'), variable=self.eddn_system, command=self.outvarchanged).grid(columnspan=2, padx=BUTTONX, sticky=tk.W) # Output setting
|
||||
# self.eddn_delay= tk.IntVar(value = (output & config.OUT_SYS_DELAY) and 1)
|
||||
# self.eddn_delay_button = nb.Checkbutton(eddnframe, text=_('Delay sending until docked'), variable=self.out_auto, command=self.outvarchanged) # Output setting under 'Send system and scan data to EDDN'
|
||||
|
||||
# notebook.add(eddnframe, text='EDDN') # Not translated
|
||||
|
||||
edsmframe = nb.Frame(notebook)
|
||||
edsmframe.columnconfigure(1, weight=1)
|
||||
|
||||
@ -174,40 +191,58 @@ class PreferencesDialog(tk.Toplevel):
|
||||
|
||||
notebook.add(edsmframe, text='EDSM') # Not translated
|
||||
|
||||
configframe = nb.Frame(notebook)
|
||||
configframe.columnconfigure(1, weight=1)
|
||||
|
||||
self.logdir = nb.Entry(configframe, takefocus=False)
|
||||
if platform != 'darwin':
|
||||
# Apple's SMB implementation is way too flaky - no filesystem events and bogus NULLs
|
||||
nb.Label(configframe, text = _('E:D log file location')+':').grid(columnspan=3, padx=PADX, sticky=tk.W) # Configuration setting
|
||||
logdir = config.get('logdir') or monitor.logdir
|
||||
if not logdir:
|
||||
pass
|
||||
elif logdir.startswith(config.home):
|
||||
self.logdir.insert(0, '~' + logdir[len(config.home):])
|
||||
else:
|
||||
self.logdir.insert(0, logdir)
|
||||
self.logdir['state'] = 'readonly'
|
||||
self.logdir.grid(row=10, columnspan=2, padx=(PADX,0), sticky=tk.EW)
|
||||
self.logbutton = nb.Button(configframe, text=(platform=='darwin' and _('Change...') or # Folder selection button on OSX
|
||||
_('Browse...')), # Folder selection button on Windows
|
||||
command = lambda:self.filebrowse(_('E:D log file location'), self.logdir))
|
||||
self.logbutton.grid(row=10, column=2, padx=PADX, sticky=tk.EW)
|
||||
nb.Button(configframe, text=_('Default'), command=self.logdir_reset, state = monitor.logdir and tk.NORMAL or tk.DISABLED).grid(column=2, padx=PADX, pady=(5,0), sticky=tk.EW) # Appearance theme and language setting
|
||||
|
||||
if platform == 'win32':
|
||||
ttk.Separator(configframe, orient=tk.HORIZONTAL).grid(columnspan=3, padx=PADX, pady=PADY*8, sticky=tk.EW)
|
||||
|
||||
if platform in ['darwin','win32']:
|
||||
self.hotkey_code = config.getint('hotkey_code')
|
||||
self.hotkey_mods = config.getint('hotkey_mods')
|
||||
self.hotkey_only = tk.IntVar(value = not config.getint('hotkey_always'))
|
||||
self.hotkey_play = tk.IntVar(value = not config.getint('hotkey_mute'))
|
||||
hotkeyframe = nb.Frame(notebook)
|
||||
hotkeyframe.columnconfigure(1, weight=1)
|
||||
nb.Label(hotkeyframe).grid(sticky=tk.W) # big spacer
|
||||
nb.Label(configframe, text = platform=='darwin' and
|
||||
_('Keyboard shortcut') or # Hotkey/Shortcut settings prompt on OSX
|
||||
_('Hotkey') # Hotkey/Shortcut settings prompt on Windows
|
||||
).grid(row=20, padx=PADX, sticky=tk.W)
|
||||
if platform == 'darwin' and not was_accessible_at_launch:
|
||||
if AXIsProcessTrusted():
|
||||
nb.Label(hotkeyframe, text = _('Re-start {APP} to use shortcuts').format(APP=applongname), foreground='firebrick').grid(padx=PADX, sticky=tk.NSEW) # Shortcut settings prompt on OSX
|
||||
nb.Label(configframe, text = _('Re-start {APP} to use shortcuts').format(APP=applongname), foreground='firebrick').grid(padx=PADX, sticky=tk.W) # Shortcut settings prompt on OSX
|
||||
else:
|
||||
nb.Label(hotkeyframe, text = _('{APP} needs permission to use shortcuts').format(APP=applongname), foreground='firebrick').grid(columnspan=2, padx=PADX, sticky=tk.W) # Shortcut settings prompt on OSX
|
||||
nb.Button(hotkeyframe, text = _('Open System Preferences'), command = self.enableshortcuts).grid(column=1, padx=PADX, sticky=tk.E) # Shortcut settings button on OSX
|
||||
nb.Label(configframe, text = _('{APP} needs permission to use shortcuts').format(APP=applongname), foreground='firebrick').grid(columnspan=3, padx=PADX, sticky=tk.W) # Shortcut settings prompt on OSX
|
||||
nb.Button(configframe, text = _('Open System Preferences'), command = self.enableshortcuts).grid(column=2, padx=PADX, sticky=tk.E) # Shortcut settings button on OSX
|
||||
else:
|
||||
self.hotkey_text = nb.Entry(hotkeyframe, width = (platform == 'darwin' and 20 or 30), justify=tk.CENTER)
|
||||
self.hotkey_text = nb.Entry(configframe, width = (platform == 'darwin' and 20 or 30), justify=tk.CENTER)
|
||||
self.hotkey_text.insert(0, self.hotkey_code and hotkeymgr.display(self.hotkey_code, self.hotkey_mods) or _('None')) # No hotkey/shortcut currently defined
|
||||
self.hotkey_text.bind('<FocusIn>', self.hotkeystart)
|
||||
self.hotkey_text.bind('<FocusOut>', self.hotkeyend)
|
||||
nb.Label(hotkeyframe, text = platform=='darwin' and
|
||||
_('Keyboard shortcut') or # Tab heading in settings on OSX
|
||||
_('Hotkey') # Tab heading in settings on Windows
|
||||
).grid(row=10, column=0, padx=PADX, sticky=tk.NSEW)
|
||||
self.hotkey_text.grid(row=10, column=1, padx=PADX, sticky=tk.NSEW)
|
||||
nb.Label(hotkeyframe).grid(sticky=tk.W) # big spacer
|
||||
self.hotkey_only_btn = nb.Checkbutton(hotkeyframe, text=_('Only when Elite: Dangerous is the active app'), variable=self.hotkey_only, state = self.hotkey_code and tk.NORMAL or tk.DISABLED) # Hotkey/Shortcut setting
|
||||
self.hotkey_only_btn.grid(columnspan=2, padx=PADX, sticky=tk.W)
|
||||
self.hotkey_play_btn = nb.Checkbutton(hotkeyframe, text=_('Play sound'), variable=self.hotkey_play, state = self.hotkey_code and tk.NORMAL or tk.DISABLED) # Hotkey/Shortcut setting
|
||||
self.hotkey_play_btn.grid(columnspan=2, padx=PADX, sticky=tk.W)
|
||||
self.hotkey_text.grid(row=20, column=1, columnspan=2, padx=PADX, pady=(5,0), sticky=tk.W)
|
||||
self.hotkey_only_btn = nb.Checkbutton(configframe, text=_('Only when Elite: Dangerous is the active app'), variable=self.hotkey_only, state = self.hotkey_code and tk.NORMAL or tk.DISABLED) # Hotkey/Shortcut setting
|
||||
self.hotkey_only_btn.grid(columnspan=3, padx=PADX, pady=(5,0), sticky=tk.W)
|
||||
self.hotkey_play_btn = nb.Checkbutton(configframe, text=_('Play sound'), variable=self.hotkey_play, state = self.hotkey_code and tk.NORMAL or tk.DISABLED) # Hotkey/Shortcut setting
|
||||
self.hotkey_play_btn.grid(columnspan=3, padx=PADX, sticky=tk.W)
|
||||
|
||||
notebook.add(hotkeyframe, text = platform=='darwin' and
|
||||
_('Keyboard shortcut') or # Tab heading in settings on OSX
|
||||
_('Hotkey')) # Tab heading in settings on Windows
|
||||
notebook.add(configframe, text=_('Configuration')) # Tab heading in settings
|
||||
|
||||
self.languages = Translations().available_names()
|
||||
self.lang = tk.StringVar(value = self.languages.get(config.get('language'), _('Default'))) # Appearance theme and language setting
|
||||
@ -223,7 +258,7 @@ class PreferencesDialog(tk.Toplevel):
|
||||
nb.Label(themeframe, text=_('Language')).grid(row=10, padx=PADX, sticky=tk.W) # Appearance setting prompt
|
||||
self.lang_button = nb.OptionMenu(themeframe, self.lang, self.lang.get(), *self.languages.values())
|
||||
self.lang_button.grid(row=10, column=1, columnspan=2, padx=PADX, sticky=tk.W)
|
||||
ttk.Separator(themeframe, orient=tk.HORIZONTAL).grid(columnspan=3, padx=PADX, pady=PADY*6, sticky=tk.EW)
|
||||
ttk.Separator(themeframe, orient=tk.HORIZONTAL).grid(columnspan=3, padx=PADX, pady=PADY*8, sticky=tk.EW)
|
||||
nb.Label(themeframe, text=_('Theme')).grid(columnspan=3, padx=PADX, sticky=tk.W) # Appearance setting
|
||||
nb.Radiobutton(themeframe, text=_('Default'), variable=self.theme, value=0, command=self.themevarchanged).grid(columnspan=3, padx=BUTTONX, sticky=tk.W) # Appearance theme and language setting
|
||||
nb.Radiobutton(themeframe, text=_('Dark'), variable=self.theme, value=1, command=self.themevarchanged).grid(columnspan=3, padx=BUTTONX, sticky=tk.W) # Appearance theme setting
|
||||
@ -235,9 +270,10 @@ class PreferencesDialog(tk.Toplevel):
|
||||
self.theme_label_1.grid(row=21, padx=PADX, sticky=tk.W)
|
||||
self.theme_button_1 = nb.ColoredButton(themeframe, text=' Hutton Orbital ', background='grey4', command=lambda:self.themecolorbrowse(1)) # Do not translate
|
||||
self.theme_button_1.grid(row=21, column=1, padx=PADX, pady=PADY, sticky=tk.NSEW)
|
||||
ttk.Separator(themeframe, orient=tk.HORIZONTAL).grid(columnspan=3, padx=PADX, pady=PADY*6, sticky=tk.EW)
|
||||
ttk.Separator(themeframe, orient=tk.HORIZONTAL).grid(columnspan=3, padx=PADX, pady=PADY*8, sticky=tk.EW)
|
||||
self.ontop_button = nb.Checkbutton(themeframe, text=_('Always on top'), variable=self.always_ontop, command=self.themevarchanged)
|
||||
self.ontop_button.grid(columnspan=3, padx=BUTTONX, sticky=tk.W) # Appearance setting
|
||||
nb.Label(themeframe).grid(sticky=tk.W) # big spacer
|
||||
|
||||
notebook.add(themeframe, text=_('Appearance')) # Tab heading in settings
|
||||
|
||||
@ -273,14 +309,15 @@ class PreferencesDialog(tk.Toplevel):
|
||||
|
||||
|
||||
def outvarchanged(self):
|
||||
self.out_auto_button['state'] = monitor.logdir and tk.NORMAL or tk.DISABLED
|
||||
logdir = self.logdir.get().startswith('~') and join(config.home, self.logdir.get()[2:]) or self.logdir.get()
|
||||
self.out_auto_button['state'] = logdir and isdir(logdir) and tk.NORMAL or tk.DISABLED
|
||||
|
||||
local = self.out_bpc.get() or self.out_td.get() or self.out_csv.get() or self.out_ship_eds.get() or self.out_ship_coriolis.get()
|
||||
self.outdir_label['state'] = local and tk.NORMAL or tk.DISABLED
|
||||
self.outbutton['state'] = local and tk.NORMAL or tk.DISABLED
|
||||
self.outdir['state'] = local and 'readonly' or tk.DISABLED
|
||||
|
||||
self.edsm_log_button['state'] = monitor.logdir and tk.NORMAL or tk.DISABLED
|
||||
self.edsm_log_button['state'] = self.logdir.get() and isdir(self.logdir.get()) and tk.NORMAL or tk.DISABLED
|
||||
|
||||
edsm_state = self.edsm_log.get() and tk.NORMAL or tk.DISABLED
|
||||
self.edsm_label['state'] = edsm_state
|
||||
@ -289,10 +326,10 @@ class PreferencesDialog(tk.Toplevel):
|
||||
self.edsm_cmdr['state'] = edsm_state
|
||||
self.edsm_apikey['state'] = edsm_state
|
||||
|
||||
def outbrowse(self):
|
||||
def filebrowse(self, title, entryfield):
|
||||
if platform != 'win32':
|
||||
import tkFileDialog
|
||||
d = tkFileDialog.askdirectory(parent=self, initialdir=expanduser(self.outdir.get()), title=_('File location'), mustexist=tk.TRUE)
|
||||
d = tkFileDialog.askdirectory(parent=self, initialdir=expanduser(entryfield.get()), title=title, mustexist=tk.TRUE)
|
||||
else:
|
||||
def browsecallback(hwnd, uMsg, lParam, lpData):
|
||||
# set initial folder
|
||||
@ -301,10 +338,10 @@ class PreferencesDialog(tk.Toplevel):
|
||||
return 0
|
||||
|
||||
browseInfo = BROWSEINFO()
|
||||
browseInfo.lpszTitle = _('File location')
|
||||
browseInfo.lpszTitle = title
|
||||
browseInfo.ulFlags = BIF_RETURNONLYFSDIRS|BIF_USENEWUI
|
||||
browseInfo.lpfn = BrowseCallbackProc(browsecallback)
|
||||
browseInfo.lParam = self.outdir.get().startswith('~') and join(config.home, self.outdir.get()[1:]) or self.outdir.get()
|
||||
browseInfo.lParam = entryfield.get().startswith('~') and join(config.home, entryfield.get()[2:]) or entryfield.get()
|
||||
ctypes.windll.ole32.CoInitialize(None)
|
||||
pidl = ctypes.windll.shell32.SHBrowseForFolderW(ctypes.byref(browseInfo))
|
||||
if pidl:
|
||||
@ -316,13 +353,26 @@ class PreferencesDialog(tk.Toplevel):
|
||||
d = None
|
||||
|
||||
if d:
|
||||
self.outdir['state'] = tk.NORMAL # must be writable to update
|
||||
self.outdir.delete(0, tk.END)
|
||||
entryfield['state'] = tk.NORMAL # must be writable to update
|
||||
entryfield.delete(0, tk.END)
|
||||
if d.startswith(config.home):
|
||||
self.outdir.insert(0, '~' + d[len(config.home):])
|
||||
entryfield.insert(0, '~' + d[len(config.home):])
|
||||
else:
|
||||
self.outdir.insert(0, d)
|
||||
self.outdir['state'] = 'readonly'
|
||||
entryfield.insert(0, d)
|
||||
entryfield['state'] = 'readonly'
|
||||
self.outvarchanged()
|
||||
|
||||
def logdir_reset(self):
|
||||
self.logdir['state'] = tk.NORMAL # must be writable to update
|
||||
self.logdir.delete(0, tk.END)
|
||||
if not monitor.logdir:
|
||||
pass
|
||||
elif monitor.logdir.startswith(config.home):
|
||||
self.logdir.insert(0, '~' + monitor.logdir[len(config.home):])
|
||||
else:
|
||||
self.logdir.insert(0, monitor.logdir)
|
||||
self.logdir['state'] = 'readonly'
|
||||
self.outvarchanged()
|
||||
|
||||
def themecolorbrowse(self, index):
|
||||
(rgb, color) = tkColorChooser.askcolor(self.theme_colors[index], title=self.theme_prompts[index], parent=self.parent)
|
||||
@ -403,6 +453,11 @@ class PreferencesDialog(tk.Toplevel):
|
||||
config.set('edsm_cmdrname', self.edsm_cmdr.get().strip())
|
||||
config.set('edsm_apikey', self.edsm_apikey.get().strip())
|
||||
|
||||
logdir = self.logdir.get().startswith('~') and join(config.home, self.logdir.get()[2:]) or self.logdir.get()
|
||||
if monitor.logdir and logdir.lower() == monitor.logdir.lower():
|
||||
config.set('logdir', '') # default location
|
||||
else:
|
||||
config.set('logdir', logdir)
|
||||
if platform in ['darwin','win32']:
|
||||
config.set('hotkey_code', self.hotkey_code)
|
||||
config.set('hotkey_mods', self.hotkey_mods)
|
||||
@ -426,8 +481,9 @@ class PreferencesDialog(tk.Toplevel):
|
||||
self.callback()
|
||||
|
||||
def _destroy(self):
|
||||
# Re-enable hotkey monitoring before exit
|
||||
# Re-enable hotkey and log monitoring before exit
|
||||
hotkeymgr.register(self.parent, config.getint('hotkey_code'), config.getint('hotkey_mods'))
|
||||
monitor.start(self.parent)
|
||||
self.parent.wm_attributes('-topmost', config.getint('always_ontop') and 1 or 0)
|
||||
self.destroy()
|
||||
|
||||
|
@ -40,7 +40,7 @@ class HyperlinkLabel(platform == 'darwin' and tk.Label or ttk.Label, object):
|
||||
|
||||
if self.underline is not False:
|
||||
self.font_n = kw.get('font', ttk.Style().lookup('TLabel', 'font'))
|
||||
self.font_u = tkFont.Font(self, self.font_n)
|
||||
self.font_u = tkFont.Font(font = self.font_n)
|
||||
self.font_u.configure(underline = True)
|
||||
if self.underline is True:
|
||||
self.configure(font = self.font_u)
|
||||
|
Loading…
x
Reference in New Issue
Block a user