2019-07-16 04:56:02 +02:00
|
|
|
#!/usr/bin/env python3
|
|
|
|
#
|
|
|
|
# This program is free software; you can redistribute it and/or modify
|
|
|
|
# it under the terms of the GNU General Public License as published by
|
|
|
|
# the Free Software Foundation; either version 2 of the License, or
|
|
|
|
# (at your option) any later version.
|
|
|
|
#
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
# GNU General Public License for more details.
|
|
|
|
#
|
|
|
|
|
|
|
|
|
|
|
|
from gi.repository import GObject
|
|
|
|
|
|
|
|
import xdg.BaseDirectory
|
|
|
|
import configparser
|
|
|
|
import logging
|
2019-07-16 05:52:54 +02:00
|
|
|
import json
|
2019-07-16 05:28:56 +02:00
|
|
|
from pathlib import Path
|
2019-07-16 04:56:02 +02:00
|
|
|
|
2019-07-18 02:10:15 +02:00
|
|
|
logger = logging.getLogger('tuhi.gui.config')
|
2019-07-16 04:56:02 +02:00
|
|
|
|
2019-08-09 06:44:51 +02:00
|
|
|
DEFAULT_CONFIG_PATH = Path(xdg.BaseDirectory.xdg_data_home, 'tuhi')
|
2019-07-16 04:56:02 +02:00
|
|
|
|
|
|
|
|
|
|
|
class Config(GObject.Object):
|
|
|
|
_config_obj = None
|
2019-08-09 06:44:51 +02:00
|
|
|
_base_path = DEFAULT_CONFIG_PATH
|
2019-07-16 04:56:02 +02:00
|
|
|
|
|
|
|
def __init__(self):
|
|
|
|
super().__init__()
|
2019-08-09 06:44:51 +02:00
|
|
|
self.path = Path(self._base_path, 'tuhigui.ini')
|
|
|
|
self.base_path = self._base_path
|
2019-07-16 04:56:02 +02:00
|
|
|
self.config = configparser.ConfigParser()
|
|
|
|
# Don't lowercase options
|
|
|
|
self.config.optionxform = str
|
2019-07-16 05:52:54 +02:00
|
|
|
self._drawings = []
|
2019-07-16 04:56:02 +02:00
|
|
|
self._load()
|
2019-07-16 05:52:54 +02:00
|
|
|
self._load_cached_drawings()
|
2019-07-16 04:56:02 +02:00
|
|
|
|
|
|
|
def _load(self):
|
2019-07-16 05:28:56 +02:00
|
|
|
if not self.path.exists():
|
2019-07-16 04:56:02 +02:00
|
|
|
return
|
|
|
|
|
|
|
|
logger.debug(f'configuration found')
|
|
|
|
self.config.read(self.path)
|
|
|
|
|
2019-07-16 05:52:54 +02:00
|
|
|
def _load_cached_drawings(self):
|
2019-08-09 06:44:51 +02:00
|
|
|
if not self.base_path.exists():
|
2019-07-16 05:52:54 +02:00
|
|
|
return
|
|
|
|
|
2019-08-09 06:44:51 +02:00
|
|
|
for filename in self.base_path.glob('*.json'):
|
2019-07-16 05:52:54 +02:00
|
|
|
with open(filename) as fd:
|
|
|
|
self._drawings.append(json.load(fd))
|
|
|
|
self.notify('drawings')
|
|
|
|
|
2019-07-16 04:56:02 +02:00
|
|
|
def _write(self):
|
2019-07-16 05:28:56 +02:00
|
|
|
self.path.resolve().parent.mkdir(parents=True, exist_ok=True)
|
2019-07-16 04:56:02 +02:00
|
|
|
with open(self.path, 'w') as fd:
|
|
|
|
self.config.write(fd)
|
|
|
|
|
2019-07-16 05:28:56 +02:00
|
|
|
def _add_key(self, section, key, value):
|
|
|
|
if section not in self.config:
|
|
|
|
self.config[section] = {}
|
|
|
|
self.config[section][key] = value
|
|
|
|
self._write()
|
|
|
|
|
2019-07-16 04:56:02 +02:00
|
|
|
@GObject.property
|
|
|
|
def orientation(self):
|
|
|
|
try:
|
|
|
|
return self.config['Device']['Orientation']
|
|
|
|
except KeyError:
|
|
|
|
return 'landscape'
|
|
|
|
|
|
|
|
@orientation.setter
|
|
|
|
def orientation(self, orientation):
|
|
|
|
assert(orientation in ['landscape', 'portrait'])
|
|
|
|
self._add_key('Device', 'Orientation', orientation)
|
|
|
|
|
2019-07-16 05:52:54 +02:00
|
|
|
@GObject.property
|
|
|
|
def drawings(self):
|
|
|
|
return self._drawings
|
|
|
|
|
|
|
|
def add_drawing(self, timestamp, json_string):
|
|
|
|
'''Add a drawing JSON with the given timestamp to the backend
|
|
|
|
storage. This will update self.drawings.'''
|
2019-08-09 06:44:51 +02:00
|
|
|
self.base_path.mkdir(parents=True, exist_ok=True)
|
2019-07-16 05:52:54 +02:00
|
|
|
|
2019-08-09 06:44:51 +02:00
|
|
|
path = Path(self.base_path, f'{timestamp}.json')
|
2019-07-16 05:52:54 +02:00
|
|
|
if path.exists():
|
|
|
|
return
|
|
|
|
|
2019-07-16 06:40:37 +02:00
|
|
|
# Tuhi may still cache files we've 'deleted' locally. These need to
|
|
|
|
# be ignored because they're still technically deleted.
|
2019-08-09 06:44:51 +02:00
|
|
|
deleted = Path(self.base_path, f'{timestamp}.json.deleted')
|
2019-07-16 06:40:37 +02:00
|
|
|
if deleted.exists():
|
|
|
|
return
|
|
|
|
|
2019-07-16 05:52:54 +02:00
|
|
|
with open(path, 'w') as fd:
|
|
|
|
fd.write(json_string)
|
|
|
|
|
|
|
|
self._drawings.append(json.loads(json_string))
|
|
|
|
self.notify('drawings')
|
|
|
|
|
2019-07-16 06:40:37 +02:00
|
|
|
def delete_drawing(self, timestamp):
|
|
|
|
# We don't delete json files immediately, we just rename them
|
|
|
|
# so we can resurrect them in the future if need be.
|
2019-08-09 06:44:51 +02:00
|
|
|
path = Path(self.base_path, f'{timestamp}.json')
|
|
|
|
target = Path(self.base_path, f'{timestamp}.json.deleted')
|
2019-07-16 06:40:37 +02:00
|
|
|
path.rename(target)
|
|
|
|
|
|
|
|
self._drawings = [d for d in self._drawings if d['timestamp'] != timestamp]
|
|
|
|
self.notify('drawings')
|
|
|
|
|
|
|
|
def undelete_drawing(self, timestamp):
|
2019-08-09 06:44:51 +02:00
|
|
|
path = Path(self.base_path, f'{timestamp}.json')
|
|
|
|
target = Path(self.base_path, f'{timestamp}.json.deleted')
|
2019-07-16 06:40:37 +02:00
|
|
|
target.rename(path)
|
|
|
|
|
|
|
|
with open(path) as fd:
|
|
|
|
self._drawings.append(json.load(fd))
|
|
|
|
self.notify('drawings')
|
|
|
|
|
2019-08-09 06:44:51 +02:00
|
|
|
@classmethod
|
|
|
|
def set_base_path(cls, path):
|
|
|
|
if cls._config_obj is not None:
|
|
|
|
logger.error('Trying to set config base path but we already have the singleton object')
|
|
|
|
return
|
|
|
|
|
|
|
|
cls._base_path = Path(path)
|
|
|
|
|
2019-07-16 04:56:02 +02:00
|
|
|
@classmethod
|
2019-07-16 13:09:02 +02:00
|
|
|
def instance(cls):
|
2019-07-16 04:56:02 +02:00
|
|
|
if cls._config_obj is None:
|
|
|
|
cls._config_obj = Config()
|
|
|
|
return cls._config_obj
|