receiver: fix and improve diversion load/save functions

This commit is contained in:
Vinícius 2020-11-14 09:54:51 -03:00 committed by Peter F. Patel-Schneider
parent 1379da70a8
commit 0b5c263799
2 changed files with 55 additions and 21 deletions

View File

@ -33,6 +33,8 @@ from Xlib import X
from Xlib.display import Display
from Xlib.ext import record
from Xlib.protocol import rq
from yaml import add_representer as _yaml_add_representer
from yaml import dump_all as _yaml_dump_all
from yaml import safe_load_all as _yaml_safe_load_all
from .common import unpack as _unpack
@ -618,6 +620,51 @@ _file_path = _path.join(_XDG_CONFIG_HOME, 'solaar', 'rules.yaml')
rules = built_in_rules
def _save_config_rule_file(file_name=_file_path):
# This is a trick to show str/float/int lists in-line (inspired by https://stackoverflow.com/a/14001707)
class inline_list(list):
pass
def blockseq_rep(dumper, data):
return dumper.represent_sequence(u'tag:yaml.org,2002:seq', data, flow_style=True)
_yaml_add_representer(inline_list, blockseq_rep)
def convert(elem):
if isinstance(elem, list):
if len(elem) == 1 and isinstance(elem[0], (int, str, float)):
# All diversion classes that expect a list of scalars also support a single scalar without a list
return elem[0]
if all(isinstance(c, (int, str, float)) for c in elem):
return inline_list([convert(c) for c in elem])
return [convert(c) for c in elem]
if isinstance(elem, dict):
return {k: convert(v) for k, v in elem.items()}
return elem
# YAML format settings
dump_settings = {
'encoding': 'utf-8',
'explicit_start': True,
'explicit_end': True,
'default_flow_style': False
# 'version': (1, 3), # it would be printed for every rule
}
# Save only user-defined rules
rules_to_save = sum([r.data()['Rule'] for r in rules.components if r.source == file_name], [])
if rules_to_save:
if _log.isEnabledFor(_INFO):
_log.info('saving %d rule(s) to %s', len(rules_to_save), file_name)
try:
with open(file_name, 'w') as f:
f.write('%YAML 1.3\n') # Write version manually
_yaml_dump_all(convert([r['Rule'] for r in rules_to_save]), f, **dump_settings)
except Exception as e:
_log.error('failed to save to %s\n%s', file_name, e)
return False
return True
def _load_config_rule_file():
global rules
loaded_rules = []
@ -634,8 +681,7 @@ def _load_config_rule_file():
_log.info('loaded %d rules from %s', len(loaded_rules), config_file.name)
except Exception as e:
_log.error('failed to load from %s\n%s', _file_path, e)
loaded_rules.append(built_in_rules)
rules = Rule(loaded_rules)
rules = Rule([Rule(loaded_rules, source=_file_path), built_in_rules])
_load_config_rule_file()

View File

@ -21,7 +21,6 @@ from __future__ import absolute_import, division, print_function, unicode_litera
from collections import defaultdict
from contextlib import contextmanager as contextlib_contextmanager
from logging import INFO as _INFO
from logging import getLogger
from shlex import quote as shlex_quote
@ -32,7 +31,6 @@ from logitech_receiver import diversion as _DIV
from logitech_receiver.special_keys import CONTROL as _CONTROL
from pynput import mouse as _mouse
from solaar.i18n import _
from yaml import dump as _yaml_dump
_log = getLogger(__name__)
del getLogger
@ -154,7 +152,7 @@ class DiversionDialog:
if response == Gtk.ResponseType.NO:
w.hide()
elif response == Gtk.ResponseType.YES:
self._save_yaml_file(_DIV._file_path)
self._save_yaml_file()
w.hide()
else:
# don't close
@ -172,21 +170,11 @@ class DiversionDialog:
self.view.set_model(self.model)
self.view.expand_all()
def _save_yaml_file(self, file_name):
rules = [r.data()['Rule'] for r in _DIV.rules.components if r.source == file_name]
if len(rules) == 1:
rules = rules[0]
if rules:
if _log.isEnabledFor(_INFO):
_log.info('saving %d rule(s) to %s', len(rules), file_name)
try:
with open(file_name, 'w') as f:
_yaml_dump(rules, f, default_flow_style=False)
self.dirty = False
self.save_btn.set_sensitive(False)
self.discard_btn.set_sensitive(False)
except Exception as e:
_log.error('failed to save to %s\n%s', file_name, e)
def _save_yaml_file(self):
if _DIV._save_config_rule_file():
self.dirty = False
self.save_btn.set_sensitive(False)
self.discard_btn.set_sensitive(False)
def _create_top_panel(self):
sw = Gtk.ScrolledWindow()
@ -205,7 +193,7 @@ class DiversionDialog:
vbox = Gtk.VBox(spacing=20)
self.save_btn = Gtk.Button(_('Save changes'), halign=Gtk.Align.CENTER, valign=Gtk.Align.CENTER, sensitive=False)
self.discard_btn = Gtk.Button(_('Discard changes'), halign=Gtk.Align.CENTER, valign=Gtk.Align.CENTER, sensitive=False)
self.save_btn.connect('clicked', lambda *_args: self._save_yaml_file(_DIV._file_path))
self.save_btn.connect('clicked', lambda *_args: self._save_yaml_file())
self.discard_btn.connect('clicked', lambda *_args: self._reload_yaml_file())
vbox.pack_start(self.save_btn, False, False, 0)
vbox.pack_start(self.discard_btn, False, False, 0)