1# This file is part of the Frescobaldi project, http://www.frescobaldi.org/
2#
3# Copyright (c) 2008 - 2014 by Wilbert Berendsen
4#
5# This program is free software; you can redistribute it and/or
6# modify it under the terms of the GNU General Public License
7# as published by the Free Software Foundation; either version 2
8# of the License, or (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18# See http://www.gnu.org/licenses/ for more information.
19
20"""
21Store meta information about documents.
22"""
23
24
25import time
26
27from PyQt5.QtCore import QSettings, QUrl
28
29import app
30import plugin
31import document
32
33
34__all__ = ["info", "define"]
35
36
37# This dictionary store the default values: "name": [default, readfunc]
38_defaults = {}
39
40
41def info(doc):
42    """Returns a MetaInfo object for the Document."""
43    return MetaInfo.instance(doc)
44
45
46def define(name, default, readfunc=None):
47    """Define a variable and its default value to be stored in the metainfo.
48
49    Should be defined before it is requested or set.
50    If readfunc is not given it defaults to a suitable function for bool or int types.
51
52    """
53    if readfunc is None:
54        if isinstance(default, bool):
55            if default:
56                readfunc = lambda v: v not in ('false', False)
57            else:
58                readfunc = lambda v: v not in ('true', True)
59        elif isinstance(default, int):
60            readfunc = int
61        else:
62            readfunc = lambda v: v
63    _defaults[name] = [default, readfunc]
64
65    # read this value for already loaded metainfo items
66    for minfo in MetaInfo.instances():
67        minfo.loadValue(name)
68
69
70class MetaInfo(plugin.DocumentPlugin):
71    """Stores meta-information for a Document."""
72    def __init__(self, doc):
73        self.load()
74        if doc.__class__ == document.EditorDocument:
75            doc.loaded.connect(self.load, -999) # before all others
76            doc.closed.connect(self.save,  999) # after all others
77
78    def settingsGroup(self):
79        url = self.document().url()
80        if not url.isEmpty():
81            s = app.settings('metainfo')
82            s.beginGroup(url.toString().replace('\\', '_').replace('/', '_'))
83            return s
84
85    def load(self):
86        s = self.settingsGroup()
87        for name in _defaults:
88            self.loadValue(name, s)
89
90    def loadValue(self, name, settings=None):
91        s = settings or self.settingsGroup()
92        default, readfunc = _defaults[name]
93        if s and QSettings().value("metainfo", True, bool):
94            self.__dict__[name] = readfunc(s.value(name, default))
95        else:
96            self.__dict__[name] = default
97
98    def save(self):
99        s = self.settingsGroup()
100        if s:
101            s.setValue("time", time.time())
102            for name in _defaults:
103                value = self.__dict__[name]
104                s.remove(name) if value == _defaults[name][0] else s.setValue(name, value)
105
106
107@app.aboutToQuit.connect
108def prune():
109    """Prune old info."""
110    s = app.settings('metainfo')
111    month_ago = time.time() - 31 * 24 * 3600
112    for key in s.childGroups():
113        if s.value(key + "/time", 0.0, float) < month_ago:
114            s.remove(key)
115