849 lines
22 KiB
Cython
849 lines
22 KiB
Cython
# Copyright 2004-2019 Tom Rothamel <pytom@bishoujo.us>
|
|
#
|
|
# Permission is hereby granted, free of charge, to any person
|
|
# obtaining a copy of this software and associated documentation files
|
|
# (the "Software"), to deal in the Software without restriction,
|
|
# including without limitation the rights to use, copy, modify, merge,
|
|
# publish, distribute, sublicense, and/or sell copies of the Software,
|
|
# and to permit persons to whom the Software is furnished to do so,
|
|
# subject to the following conditions:
|
|
#
|
|
# The above copyright notice and this permission notice shall be
|
|
# included in all copies or substantial portions of the Software.
|
|
#
|
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
from __future__ import print_function
|
|
|
|
from cpython.ref cimport PyObject, Py_XDECREF
|
|
from libc.string cimport memset
|
|
from libc.stdlib cimport calloc, free
|
|
|
|
import renpy
|
|
|
|
include "styleconstants.pxi"
|
|
|
|
################################################################################
|
|
# Property Functions
|
|
################################################################################
|
|
|
|
# A class that wraps a pointer to a property function.
|
|
cdef class PropertyFunctionWrapper:
|
|
cdef property_function function
|
|
|
|
# A dictionary that maps the name of a property function into a
|
|
# PropertyFunctionWrapper.
|
|
cdef dict property_functions = { }
|
|
|
|
cdef void register_property_function(name, property_function function):
|
|
"""
|
|
Registers `function` to be the property function called for the
|
|
property `name`.
|
|
"""
|
|
|
|
cdef PropertyFunctionWrapper pfw
|
|
|
|
pfw = PropertyFunctionWrapper()
|
|
pfw.function = function
|
|
property_functions[name] = pfw
|
|
|
|
|
|
################################################################################
|
|
# Style Management
|
|
################################################################################
|
|
|
|
# A map from style name (a tuple) to the style object with that name.
|
|
styles = { }
|
|
|
|
cpdef get_style(name):
|
|
"""
|
|
Gets the style with `name`, which must be a string.
|
|
|
|
If the style doesn't exist, and it contains an underscore in it, creates
|
|
a new style that has as a parent the part after the first underscore, if
|
|
a parent with that name exists.
|
|
"""
|
|
|
|
nametuple = (name,)
|
|
|
|
rv = styles.get(nametuple, None)
|
|
|
|
if rv is not None:
|
|
return rv
|
|
|
|
start, _mid, end = name.partition("_")
|
|
|
|
if not end:
|
|
raise Exception("Style %r does not exist." % name)
|
|
|
|
# Deal with inheritance of styles beginning with _ a bit specially,
|
|
# so _foo_bar inherits from _bar, not bar.
|
|
if not start:
|
|
_start, _mid, end = name[1:].partition("_")
|
|
|
|
if not end:
|
|
raise Exception("Style %r does not exist." % name)
|
|
|
|
end = "_" + end
|
|
|
|
try:
|
|
parent = get_style(end)
|
|
except:
|
|
raise Exception("Style %r does not exist." % name)
|
|
|
|
rv = Style(parent, name=nametuple)
|
|
styles[nametuple] = rv
|
|
return rv
|
|
|
|
cpdef get_or_create_style(name):
|
|
"""
|
|
Like get_style, but if the style doesn't exist, its hierarchy is created,
|
|
eventually inheriting from default.
|
|
"""
|
|
|
|
nametuple = (name,)
|
|
|
|
rv = styles.get(nametuple, None)
|
|
if rv is not None:
|
|
return rv
|
|
|
|
start, _mid, end = name.partition("_")
|
|
|
|
# We need both sides of the _, as we don't want to have
|
|
# _foo auto-inherit from foo.
|
|
if not start or not end:
|
|
rv = Style("default", name=nametuple)
|
|
styles[nametuple] = rv
|
|
return rv
|
|
|
|
parent = get_or_create_style(end)
|
|
|
|
rv = Style(parent, name=nametuple)
|
|
styles[nametuple] = rv
|
|
return rv
|
|
|
|
|
|
cpdef get_full_style(name):
|
|
"""
|
|
Gets the style with `name`, which must be a tuple.
|
|
"""
|
|
|
|
|
|
rv = styles.get(name, None)
|
|
if rv is not None:
|
|
return rv
|
|
|
|
rv = get_style(name[0])
|
|
|
|
for i in name[1:]:
|
|
rv = rv[i]
|
|
|
|
return rv
|
|
|
|
|
|
cpdef get_tuple_name(s):
|
|
"""
|
|
Gets the tuple name of a style, where `s` may be a tuple, Style, or string.
|
|
|
|
If `s` is None, returns None.
|
|
"""
|
|
|
|
if isinstance(s, StyleCore):
|
|
return s.name
|
|
elif isinstance(s, tuple):
|
|
return s
|
|
elif s is None:
|
|
return s
|
|
else:
|
|
return (s,)
|
|
|
|
|
|
def get_text_style(style, default):
|
|
"""
|
|
If style exists, returns `style` + "_text". Otherwise, returns `default`.
|
|
For indexed styles, the above is applied first, and then indexing is applied.
|
|
"""
|
|
|
|
if style is None:
|
|
style = default
|
|
|
|
style = get_tuple_name(style)
|
|
|
|
start = style[0]
|
|
rest = style[1:]
|
|
|
|
rv = get_full_style((start + "_text",))
|
|
|
|
for i in rest:
|
|
rv = rv[i]
|
|
|
|
return rv
|
|
|
|
|
|
class StyleManager(object):
|
|
"""
|
|
The object exported as style in the store.
|
|
"""
|
|
|
|
def __setattr__(self, name, value):
|
|
|
|
if not isinstance(value, StyleCore):
|
|
|
|
if getattr(value, "_is_style_compat", False):
|
|
self.__dict__[name] = value
|
|
return
|
|
|
|
raise Exception("Value is not a style.")
|
|
|
|
cdef StyleCore style = value
|
|
|
|
name = (name,)
|
|
|
|
if style.name is None:
|
|
style.name = name
|
|
|
|
styles[name] = value
|
|
|
|
__setitem__ = __setattr__
|
|
|
|
def __getattr__(self, name):
|
|
return get_style(name)
|
|
|
|
__getitem__ = __getattr__
|
|
|
|
def create(self, name, parent, description=None):
|
|
"""
|
|
Deprecated way of creating styles.
|
|
"""
|
|
|
|
s = Style(parent, help=description)
|
|
self[name] = s
|
|
|
|
def rebuild(self):
|
|
renpy.style.rebuild()
|
|
|
|
def exists(self, name):
|
|
"""
|
|
Returns `true` if name is a style.
|
|
"""
|
|
|
|
return (name in styles) or ((name,) in styles)
|
|
|
|
def get(self, name):
|
|
"""
|
|
Gets a style, which may be a name or a tuple.
|
|
"""
|
|
|
|
if isinstance(name, tuple):
|
|
return get_full_style(name)
|
|
else:
|
|
return get_style(name)
|
|
|
|
|
|
################################################################################
|
|
# Style Class
|
|
################################################################################
|
|
|
|
def style_name_to_string(name):
|
|
|
|
if name is None:
|
|
return "anonymous style"
|
|
|
|
rv = "style " + name[0]
|
|
|
|
for i in name[1:]:
|
|
rv += "['{}']".format(i)
|
|
|
|
return rv
|
|
|
|
cdef class StyleCore:
|
|
|
|
def __init__(self, parent, properties=None, name=None, help=None, heavy=True):
|
|
"""
|
|
`parent`
|
|
The parent of this style. One of:
|
|
|
|
* A Style object.
|
|
* A string giving the name of a style.
|
|
* A tuple giving the name of an indexed style.
|
|
* None, to indicate there is no parent.
|
|
|
|
`properties`
|
|
A map from style property to its value.
|
|
|
|
`name`
|
|
If given, a tuple that will be the name of this style.
|
|
|
|
`help`
|
|
Help information for this style.
|
|
|
|
`heavy`
|
|
Ignored, but retained for compatibility.
|
|
"""
|
|
|
|
self.prefix = "insensitive_"
|
|
self.prefix_offset = INSENSITIVE_PREFIX
|
|
|
|
self.properties = [ ]
|
|
|
|
if properties:
|
|
if not type(properties) is dict:
|
|
properties = dict(properties)
|
|
|
|
self.properties.append(properties)
|
|
|
|
if properties and ("insensitive_child" in properties):
|
|
if properties["insensitive_child"] is False:
|
|
import traceback
|
|
traceback.print_stack()
|
|
|
|
self.parent = get_tuple_name(parent)
|
|
self.name = name
|
|
self.help = help
|
|
|
|
def copy(self):
|
|
cdef StyleCore rv
|
|
|
|
rv = Style(self.parent)
|
|
rv.properties = list(self.properties)
|
|
return rv
|
|
|
|
def __richcmp__(self, o, int op):
|
|
if self is o:
|
|
eq = True
|
|
elif type(self) != type(o):
|
|
eq = False
|
|
elif self.parent != o.parent:
|
|
eq = False
|
|
elif self.name != o.name:
|
|
eq = False
|
|
elif self.properties != o.properties:
|
|
eq = False
|
|
else:
|
|
eq = True
|
|
|
|
if op == 2: # ==
|
|
return eq
|
|
elif op == 3: # !=
|
|
return not eq
|
|
else:
|
|
return NotImplemented
|
|
|
|
def __dealloc__(self):
|
|
unbuild_style(self)
|
|
|
|
def __getstate__(self):
|
|
|
|
rv = dict(
|
|
properties=self.properties,
|
|
prefix=self.prefix,
|
|
name=self.name,
|
|
parent=self.parent)
|
|
|
|
return rv
|
|
|
|
def __setstate__(self, state):
|
|
|
|
self.properties = state["properties"]
|
|
self.name = state["name"]
|
|
self.set_parent(state["parent"])
|
|
self.set_prefix(state["prefix"])
|
|
|
|
def __repr__(self):
|
|
if self.parent:
|
|
return "<{} is {} @ {}>".format(style_name_to_string(self.name), style_name_to_string(self.parent), hex(id(self)))
|
|
else:
|
|
return "<{} @ {}>".format(style_name_to_string(self.name), hex(id(self)))
|
|
|
|
def __getitem__(self, name):
|
|
tname = self.name + (name,)
|
|
|
|
rv = styles.get(tname, None)
|
|
if rv is not None:
|
|
return rv
|
|
|
|
if self.parent is not None:
|
|
parent = self.parent + (name,)
|
|
else:
|
|
parent = None
|
|
|
|
rv = Style(parent, name=tname)
|
|
styles[tname] = rv
|
|
return rv
|
|
|
|
def setattr(self, property, value): # @ReservedAssignment
|
|
self.properties.append({ property : value })
|
|
|
|
def delattr(self, property): # @ReservedAssignment
|
|
for d in self.properties:
|
|
if property in d:
|
|
del d[property]
|
|
|
|
def __setattr__(self, name, value):
|
|
if name not in prefixed_all_properties:
|
|
raise Exception("Style property {} is not known.".format(name))
|
|
self.properties.append({ name : value })
|
|
|
|
def __delattr__(self, name):
|
|
self.delattr(name)
|
|
|
|
def set_parent(self, parent):
|
|
self.parent = get_tuple_name(parent)
|
|
|
|
def clear(self):
|
|
self.properties = [ ]
|
|
|
|
def take(self, other):
|
|
"""
|
|
Takes the style properties from `other`, which may be a style name
|
|
string or a style object.
|
|
"""
|
|
|
|
if not isinstance(other, StyleCore):
|
|
other = get_style(other)
|
|
|
|
self.properties = copy_properties(other.properties)
|
|
|
|
def setdefault(self, **properties):
|
|
"""
|
|
This sets the default value of the given properties, if no more
|
|
explicit values have been set.
|
|
"""
|
|
|
|
for p in self.properties:
|
|
for k in p:
|
|
if k in properties:
|
|
del properties[k]
|
|
|
|
if properties:
|
|
self.properties.append(properties)
|
|
|
|
def add_properties(self, properties):
|
|
"""
|
|
Adds the properties (which must be a dict) to this style.
|
|
"""
|
|
|
|
self.properties.append(dict(properties))
|
|
|
|
def set_prefix(self, prefix):
|
|
"""
|
|
Sets the style_prefix to `prefix`.
|
|
"""
|
|
|
|
if prefix == self.prefix:
|
|
return
|
|
|
|
self.prefix = prefix
|
|
|
|
if prefix == "insensitive_":
|
|
self.prefix_offset = INSENSITIVE_PREFIX
|
|
elif prefix == "idle_":
|
|
self.prefix_offset = IDLE_PREFIX
|
|
elif prefix == "hover_":
|
|
self.prefix_offset = HOVER_PREFIX
|
|
elif prefix == "selected_insensitive_":
|
|
self.prefix_offset = SELECTED_INSENSITIVE_PREFIX
|
|
elif prefix == "selected_idle_":
|
|
self.prefix_offset = SELECTED_IDLE_PREFIX
|
|
elif prefix == "selected_hover_":
|
|
self.prefix_offset = SELECTED_HOVER_PREFIX
|
|
|
|
def get_offset(self):
|
|
return self.prefix_offset
|
|
|
|
def get_placement(self):
|
|
"""
|
|
Returns a tuple giving the placement of the object.
|
|
"""
|
|
return (
|
|
self._get(XPOS_INDEX),
|
|
self._get(YPOS_INDEX),
|
|
self._get(XANCHOR_INDEX),
|
|
self._get(YANCHOR_INDEX),
|
|
self._get(XOFFSET_INDEX),
|
|
self._get(YOFFSET_INDEX),
|
|
self._get(SUBPIXEL_INDEX),
|
|
)
|
|
|
|
cpdef _get(StyleCore self, int index):
|
|
"""
|
|
Retrieves the property at `index` from this style or its parents.
|
|
"""
|
|
|
|
cdef PyObject *o
|
|
|
|
# The current style object we're looking at.
|
|
cdef StyleCore s
|
|
|
|
# The style object we'll backtrack to when s has no down-parent.
|
|
cdef StyleCore left
|
|
|
|
# A limit to the number of styles we'll consider.
|
|
cdef int limit
|
|
|
|
index += self.prefix_offset
|
|
|
|
if not self.built:
|
|
build_style(self)
|
|
|
|
s = self
|
|
left = None
|
|
limit = 100
|
|
|
|
while limit > 0:
|
|
|
|
# If we have the style, return it.
|
|
if s.cache != NULL:
|
|
o = s.cache[index]
|
|
if o != NULL:
|
|
return <object> o
|
|
|
|
# If there is no left-parent, and we have one, store it.
|
|
if left is None and s.left_parent is not None:
|
|
left = s.left_parent
|
|
|
|
s = s.down_parent
|
|
|
|
# If no down-parent, try left.
|
|
if s is None:
|
|
s = left
|
|
left = None
|
|
|
|
# If no down-parent or left-parent, default to None.
|
|
if s is None:
|
|
return None
|
|
|
|
limit -= 1
|
|
|
|
raise Exception("{} is too complex. Check for loops in style inheritance.".format(self))
|
|
|
|
|
|
cpdef _get_unoffset(self, int index):
|
|
return self._get(index - self.prefix_offset)
|
|
|
|
|
|
def _visit_window(self, pd):
|
|
"""
|
|
Predicts properties for a window.
|
|
|
|
`pd`
|
|
The function that should be called to predict a displayable.
|
|
"""
|
|
|
|
for i in [ INSENSITIVE_PREFIX, IDLE_PREFIX, HOVER_PREFIX, SELECTED_INSENSITIVE_PREFIX, SELECTED_IDLE_PREFIX, SELECTED_HOVER_PREFIX ]:
|
|
for j in [ BACKGROUND_INDEX, CHILD_INDEX, FOREGROUND_INDEX ]:
|
|
v = self._get_unoffset(i + j)
|
|
if v is not None:
|
|
pd(v)
|
|
|
|
def _visit_bar(self, pd):
|
|
"""
|
|
Predicts properties for a window.
|
|
|
|
`pd`
|
|
The function that should be called to predict a displayable.
|
|
"""
|
|
|
|
for i in [ INSENSITIVE_PREFIX, IDLE_PREFIX, HOVER_PREFIX, SELECTED_INSENSITIVE_PREFIX, SELECTED_IDLE_PREFIX, SELECTED_HOVER_PREFIX ]:
|
|
for j in [ FORE_BAR_INDEX, AFT_BAR_INDEX, THUMB_INDEX, THUMB_SHADOW_INDEX ]:
|
|
v = self._get_unoffset(i + j)
|
|
if v is not None:
|
|
pd(v)
|
|
|
|
def _visit_frame(self, pd):
|
|
"""
|
|
Predicts properties for a Frame.
|
|
|
|
`pd`
|
|
The function that should be called to predict a displayable.
|
|
"""
|
|
|
|
for i in [ INSENSITIVE_PREFIX, IDLE_PREFIX, HOVER_PREFIX, SELECTED_INSENSITIVE_PREFIX, SELECTED_IDLE_PREFIX, SELECTED_HOVER_PREFIX ]:
|
|
for j in [ CHILD_INDEX ]:
|
|
v = self._get_unoffset(i + j)
|
|
if v is not None:
|
|
pd(v)
|
|
|
|
def inspect(StyleCore self):
|
|
"""
|
|
Inspects this style.
|
|
|
|
Returns a list of (name, properties) pairs for each style, with only
|
|
properties that affect the final result being in the properties
|
|
list. Properties is a map from property name to value.
|
|
"""
|
|
|
|
cdef StyleCore s
|
|
cdef StyleCore left
|
|
|
|
init_inspect()
|
|
|
|
if not self.built:
|
|
build_style(self)
|
|
|
|
rv = [ ]
|
|
|
|
# Affected properties that we've seen already.
|
|
seen_properties = set()
|
|
|
|
def inspect_one(s):
|
|
|
|
my_properties = { }
|
|
|
|
for pdict in reversed(s.properties):
|
|
|
|
propnames = list(pdict)
|
|
propnames.sort(key=lambda pn : priority.get(pn, 100))
|
|
propnames.reverse()
|
|
|
|
for propname in propnames:
|
|
prop_affects = affects.get(propname, [ ])
|
|
|
|
for a in prop_affects:
|
|
if a not in seen_properties:
|
|
break
|
|
else:
|
|
continue
|
|
|
|
for a in prop_affects:
|
|
seen_properties.add(a)
|
|
|
|
my_properties[propname] = pdict[propname]
|
|
|
|
rv.append((s.name, my_properties))
|
|
|
|
s = self
|
|
left = None
|
|
|
|
while True:
|
|
|
|
inspect_one(s)
|
|
|
|
# If there is no left-parent, and we have one, store it.
|
|
if left is None and s.left_parent is not None:
|
|
left = s.left_parent
|
|
|
|
s = s.down_parent
|
|
|
|
# If no down-parent, try left.
|
|
if s is None:
|
|
s = left
|
|
left = None
|
|
|
|
# If no down-parent or left-parent, default to None.
|
|
if s is None:
|
|
break
|
|
|
|
return rv
|
|
|
|
|
|
# This will be replaced when renpy.styledata.import_style_functions is called.
|
|
Style = StyleCore
|
|
|
|
from renpy.styledata.stylesets import all_properties, prefix_priority, prefix_alts, property_priority
|
|
|
|
# The set of all prefixed properties we know about.
|
|
prefixed_all_properties = {
|
|
prefix + propname
|
|
for prefix in prefix_priority
|
|
for propname in all_properties
|
|
}
|
|
|
|
################################################################################
|
|
# Building
|
|
################################################################################
|
|
|
|
cpdef build_style(StyleCore s):
|
|
cdef int cache_priorities[PREFIX_COUNT * STYLE_PROPERTY_COUNT]
|
|
cdef dict d
|
|
cdef PropertyFunctionWrapper pfw
|
|
|
|
if s.built:
|
|
return
|
|
|
|
if s.building and s.name:
|
|
raise Exception("{} is part of a loop of recursive styles (is one of its own parents).".format(style_name_to_string(s.name)))
|
|
|
|
s.building = True
|
|
|
|
try:
|
|
|
|
# Find our parents.
|
|
if s.parent is not None:
|
|
s.down_parent = get_full_style(s.parent)
|
|
build_style(s.down_parent)
|
|
|
|
if s.name is not None and len(s.name) > 1:
|
|
s.left_parent = get_full_style(s.name[:-1])
|
|
build_style(s.left_parent)
|
|
|
|
# Build the properties cache.
|
|
if not s.properties:
|
|
s.cache = NULL
|
|
return
|
|
|
|
memset(cache_priorities, 0, sizeof(int) * PREFIX_COUNT * STYLE_PROPERTY_COUNT)
|
|
|
|
s.cache = <PyObject **> calloc(PREFIX_COUNT * STYLE_PROPERTY_COUNT, sizeof(PyObject *))
|
|
|
|
priority = 1
|
|
|
|
for d in s.properties:
|
|
for k, v in d.items():
|
|
|
|
pfw = property_functions.get(k, None)
|
|
|
|
if pfw is None:
|
|
continue
|
|
|
|
try:
|
|
pfw.function(s.cache, cache_priorities, priority, v)
|
|
except:
|
|
renpy.game.exception_info = "While processing the {} property of {}:".format(k, style_name_to_string(s.name))
|
|
raise
|
|
|
|
priority += PRIORITY_LEVELS
|
|
|
|
s.built = True
|
|
|
|
finally:
|
|
s.building = False
|
|
|
|
cpdef unbuild_style(StyleCore s):
|
|
cdef int i
|
|
|
|
if not s.built:
|
|
return
|
|
|
|
if s.cache != NULL:
|
|
|
|
for 0 <= i < PREFIX_COUNT * STYLE_PROPERTY_COUNT:
|
|
Py_XDECREF(s.cache[i])
|
|
|
|
free(s.cache)
|
|
s.cache = NULL
|
|
|
|
s.left_parent = None
|
|
s.down_parent = None
|
|
|
|
s.built = False
|
|
s.building = False
|
|
|
|
################################################################################
|
|
# Inspect support
|
|
################################################################################
|
|
|
|
# A map from prefixed property to priority.
|
|
priority = None
|
|
|
|
# A map from prefixed property to the prefixed properties it affects.
|
|
affects = None
|
|
|
|
def init_inspect():
|
|
|
|
global priority
|
|
global affects
|
|
|
|
if priority is not None:
|
|
return
|
|
|
|
priority = { }
|
|
affects = { }
|
|
|
|
for prefixname, pri in prefix_priority.items():
|
|
for propname, proplist in all_properties.items():
|
|
priority[prefixname + propname] = pri + property_priority.get(propname, 0)
|
|
affects[prefixname + propname] = [ a + i for a in prefix_alts[prefixname] for i in proplist ]
|
|
|
|
|
|
################################################################################
|
|
# Other functions
|
|
################################################################################
|
|
|
|
def reset():
|
|
"""
|
|
Reset the style system.
|
|
"""
|
|
|
|
styles.clear()
|
|
|
|
|
|
def build_styles():
|
|
"""
|
|
Builds or rebuilds all styles.
|
|
"""
|
|
for i in renpy.config.build_styles_callbacks:
|
|
i()
|
|
|
|
for s in styles.values():
|
|
unbuild_style(s)
|
|
|
|
for s in styles.values():
|
|
build_style(s)
|
|
|
|
def rebuild(prepare_screens=True):
|
|
"""
|
|
Rebuilds all styles.
|
|
"""
|
|
|
|
build_styles()
|
|
|
|
renpy.display.screen.prepared = False
|
|
|
|
if not renpy.game.context().init_phase:
|
|
renpy.display.screen.prepare_screens()
|
|
|
|
renpy.exports.restart_interaction()
|
|
|
|
def copy_properties(p):
|
|
"""
|
|
Makes a copy of the properties dict p.
|
|
"""
|
|
|
|
return [ dict(i) for i in p ]
|
|
|
|
def backup():
|
|
"""
|
|
Returns an opaque object that backs up the current styles.
|
|
"""
|
|
|
|
rv = { }
|
|
|
|
for k, v in styles.iteritems():
|
|
rv[k] = (v.parent, copy_properties(v.properties))
|
|
|
|
return rv
|
|
|
|
def restore(o):
|
|
"""
|
|
Restores a style backup.
|
|
"""
|
|
|
|
cdef StyleCore s
|
|
|
|
keys = list(styles.keys())
|
|
|
|
for i in keys:
|
|
if i not in o:
|
|
del styles[i]
|
|
|
|
|
|
for k, v in o.iteritems():
|
|
|
|
s = get_or_create_style(k[0])
|
|
|
|
for i in k[1:]:
|
|
s = s[i]
|
|
|
|
parent, properties = v
|
|
|
|
s.clear()
|
|
s.set_parent(parent)
|
|
s.properties = copy_properties(properties)
|
|
|