Anki/anki/models.py
Damien Elmes 0e084f8e3d refactor font rewriting
Our goal is to allow decks created on one platform to use similar fonts (even
if named differently) when moving to another platform. The old solution wasn't
useful for the web version or mobile versions. Instead, we store a mapping in
the deck, and when generating the CSS, we list all possible fonts. An option
in the interface for the user to add extra fonts might be nice.
2011-04-28 09:23:54 +09:00

209 lines
5.8 KiB
Python

# -*- coding: utf-8 -*-
# Copyright: Damien Elmes <anki@ichi2.net>
# License: GNU GPL, version 3 or later; http://www.gnu.org/copyleft/gpl.html
import simplejson
from anki.utils import intTime, hexifyID
from anki.lang import _
# Models
##########################################################################
defaultConf = {
}
class Model(object):
def __init__(self, deck, id=None):
self.deck = deck
if id:
self.id = id
self.load()
else:
self.id = None
self.name = u""
self.mod = intTime()
self.conf = defaultConf.copy()
self.css = ""
self.fields = []
self.templates = []
def load(self):
(self.mod,
self.name,
self.fields,
self.conf) = self.deck.db.first("""
select mod, name, flds, conf from models where id = ?""", self.id)
self.fields = simplejson.loads(self.fields)
self.conf = simplejson.loads(self.conf)
self.loadTemplates()
def flush(self):
self.mod = intTime()
ret = self.deck.db.execute("""
insert or replace into models values (?, ?, ?, ?, ?, ?)""",
self.id, self.mod, self.name,
simplejson.dumps(self.fields),
simplejson.dumps(self.conf),
self.genCSS())
self.id = ret.lastrowid
[t._flush() for t in self.templates]
def updateCache(self):
self.deck.updateCache([self.id], "model")
def _getID(self):
if not self.id:
# flush so we can get our DB id
self.flush()
return self.id
# Fields
##################################################
def newField(self):
return defaultFieldConf.copy()
def addField(self, field):
self.deck.modSchema()
self.fields.append(field)
def fieldMap(self):
"Mapping of field name -> (ord, conf)."
return dict([(f['name'], (c, f)) for c, f in enumerate(self.fields)])
def sortField(self):
return 0
# Templates
##################################################
def loadTemplates(self):
sql = "select * from templates where mid = ? order by ord"
self.templates = [Template(self.deck, data)
for data in self.deck.db.all(sql, self.id)]
def addTemplate(self, template):
self.deck.modSchema()
template.mid = self._getID()
template.ord = len(self.templates)
self.templates.append(template)
# Copying
##################################################
def copy(self):
"Copy, flush and return."
new = Model(self.deck, self.id)
new.id = None
new.name += _(" copy")
new.fields = [f.copy() for f in self.fields]
# get new id
t = new.templates; new.templates = []
new.flush()
# then put back
new.templates = t
for t in new.templates:
t.id = None
t.mid = new.id
t._flush()
return new
# CSS generation
##################################################
def genCSS(self):
if not self.id:
return ""
# fields
css = "".join([self._fieldCSS(
".fm%s.%s" % (hexifyID(self.id), hexifyID(c)),
(f['font'], f['qsize'], f['qcol'], f['rtl'], f['pre']))
for c, f in enumerate(self.fields)])
# templates
for t in self.templates:
if not t.id:
# not flushed yet, ignore for now
continue
css += "#cm%s {text-align:%s;background:%s}\n" % (
hexifyID(t.id),
("center", "left", "right")[t.conf['align']],
t.conf['bg'])
return css
def _rewriteFont(self, font):
"Convert a platform font to a multiplatform list."
font = font.lower()
for family in self.deck.conf['fontFamilies']:
for font2 in family:
if font == font2.lower():
return ",".join(family)
return font
def _fieldCSS(self, prefix, row):
(fam, siz, col, rtl, pre) = row
t = 'font-family:"%s";' % self._rewriteFont(fam)
t += 'font-size:%dpx;' % siz
t += 'color:%s;' % col
if rtl:
t += "direction:rtl;unicode-bidi:embed;"
if pre:
t += "white-space:pre-wrap;"
t = "%s {%s}\n" % (prefix, t)
return t
# Field object
##########################################################################
defaultFieldConf = {
'name': "",
'rtl': False,
'req': False,
'uniq': False,
'font': "Arial",
'qsize': 20,
'esize': 20,
'qcol': "#fff",
'pre': True,
}
# Template object
##########################################################################
defaultTemplateConf = {
'hideQ': False,
'align': 0,
'bg': "#000",
'allowEmptyAns': None,
'typeAnswer': None,
}
class Template(object):
def __init__(self, deck, data=None):
self.deck = deck
if data:
self.initFromData(data)
else:
self.id = None
self.active = True
self.conf = defaultTemplateConf.copy()
def initFromData(self, data):
(self.id,
self.mid,
self.ord,
self.name,
self.active,
self.qfmt,
self.afmt,
self.conf) = data
self.conf = simplejson.loads(self.conf)
def _flush(self):
ret = self.deck.db.execute("""
insert or replace into templates values (?, ?, ?, ?, ?, ?, ?, ?)""",
self.id, self.mid, self.ord, self.name,
self.active, self.qfmt, self.afmt,
simplejson.dumps(self.conf))
self.id = ret.lastrowid