aboutsummaryrefslogtreecommitdiffstats
path: root/Lib
diff options
context:
space:
mode:
Diffstat (limited to 'Lib')
-rw-r--r--Lib/defconQt/LayerSetList.py135
-rw-r--r--Lib/defconQt/fontView.py8
-rw-r--r--Lib/defconQt/glyphView.py722
-rw-r--r--Lib/defconQt/icons_db.py478
-rw-r--r--Lib/defconQt/icons_db.qrc1
-rw-r--r--Lib/defconQt/layerSetList.py135
-rw-r--r--Lib/defconQt/representationFactories/qPainterPathFactory.py2
-rw-r--r--Lib/defconQt/resources/defaultColor.svg62
8 files changed, 1249 insertions, 294 deletions
diff --git a/Lib/defconQt/LayerSetList.py b/Lib/defconQt/LayerSetList.py
new file mode 100644
index 0000000..b2d3c9f
--- /dev/null
+++ b/Lib/defconQt/LayerSetList.py
@@ -0,0 +1,135 @@
+from PyQt5.QtCore import Qt
+from PyQt5.QtGui import QKeySequence, QColor, QPixmap, QIcon
+from PyQt5.QtWidgets import QWidget, QMenu, QListWidget, QListWidgetItem, \
+ QAbstractItemView, QVBoxLayout, QAction, QColorDialog
+from defconQt import icons_db
+from defconQt.glyphView import AddLayerDialog
+
+class LayerSetList(QListWidget):
+ def __init__(self, font, parent=None, *args, **kwds):
+ super().__init__(parent, *args, **kwds)
+
+ self._layerSet = font.layers
+
+ self.setDragDropMode(QAbstractItemView.InternalMove)
+
+ model = self.model()
+ model.rowsMoved.connect(self._reordered)
+ self.itemChanged.connect(self._itemChanged)
+
+ self.setContextMenuPolicy(Qt.ActionsContextMenu)
+
+ action = QAction("Add Layer…", self)
+ action.setShortcuts(QKeySequence.New)
+ action.triggered.connect(self._addLayer)
+ self.addAction(action)
+
+ action = QAction("Change &Name", self)
+ action.setShortcuts(QKeySequence('n'))
+ action.triggered.connect(lambda : self.editItem(self.currentItem()))
+ self.addAction(action)
+
+ action = QAction("Change &Color", self)
+ action.setShortcuts(QKeySequence('c'))
+ action.triggered.connect(self._changeColor)
+ self.addAction(action)
+
+ action = QAction("Reset Color to &Default", self)
+ action.setShortcuts(QKeySequence('d'))
+ action.triggered.connect(self._resetColor)
+ self.addAction(action)
+
+ action = QAction("Delete", self)
+ action.setShortcuts(QKeySequence.Delete)
+ action.triggered.connect(self._deleteLayer)
+ self.addAction(action)
+
+ self._layerSet.addObserver(self, '_update', 'LayerSet.Changed')
+
+ self._update()
+
+ def _update(self, *args):
+ index = self.currentRow()
+ while self.count():
+ self.takeItem(self.count()-1)
+ for i, layer in enumerate(self._layerSet):
+ item = self._makeItem(layer)
+ self.addItem(item)
+ if i == index:
+ self.setCurrentItem(item)
+
+ def _makeItem(self, layer):
+ isDefault = layer is self._layerSet.defaultLayer
+ name = layer.name
+ color = layer.color
+ item = QListWidgetItem()
+ item.setText(name)
+ if color:
+ pixmap = QPixmap(100, 100)
+ # change color
+ pixmap.fill(QColor.fromRgbF(*color))
+ icon = QIcon(pixmap)
+ else:
+ icon = QIcon(":/resources/defaultColor.svg")
+ item.setIcon(icon)
+
+ if isDefault:
+ font = item.font()
+ font.setBold(True)
+ item.setFont(font)
+
+ item.setFlags(item.flags() | Qt.ItemIsEditable)
+
+ return item;
+
+ def _getCurrentLayer(self):
+ item = self.currentItem()
+ if not item:
+ return
+ name = item.text()
+ return self._layerSet[name] if name in self._layerSet else None
+
+ def _deleteLayer(self):
+ layer = self._getCurrentLayer()
+ if not layer:
+ return
+
+ if layer is self._layerSet.defaultLayer:
+ # because I think we can't handle a font without a default layer
+ # TODO: try this
+ return
+ del self._layerSet[layer.name];
+
+ def _reordered(self, *args):
+ # get a new layer order
+ newOrder = [self.item(index).text() for index in range(self.count())]
+ self._layerSet.layerOrder = newOrder
+
+ def _itemChanged(self, item):
+ index = self.indexFromItem(item).row()
+ layerName = self._layerSet.layerOrder[index]
+ self._layerSet[layerName].name = item.text()
+
+ def _addLayer(self):
+ newLayerName, ok = AddLayerDialog.getNewLayerName(self)
+ if ok:
+ # this should cause self._layerSetLayerAdded to be executed
+ self._layerSet.newLayer(newLayerName)
+
+ def _changeColor(self):
+ layer = self._getCurrentLayer()
+ if not layer:
+ return
+
+ startColor = layer.color and QColor.fromRgbF(*layer.color) or QColor('limegreen')
+ qcolor = QColorDialog.getColor(startColor, self, options=QColorDialog.ShowAlphaChannel)
+ if not qcolor.isValid():
+ # cancelled
+ return
+ layer.color = qcolor.getRgbF()
+
+ def _resetColor(self):
+ layer = self._getCurrentLayer()
+ if not layer:
+ return
+ layer.color = None
diff --git a/Lib/defconQt/fontView.py b/Lib/defconQt/fontView.py
index 7c83d51..001d2ea 100644
--- a/Lib/defconQt/fontView.py
+++ b/Lib/defconQt/fontView.py
@@ -3,6 +3,7 @@ from defconQt.fontInfo import TabDialog
from defconQt.glyphCollectionView import GlyphCollectionWidget
from defconQt.glyphView import MainGfxWindow
from defconQt.groupsView import GroupsWindow
+from defconQt.layerSetList import LayerSetList
from defconQt.scriptingWindow import MainScriptingWindow
from defconQt.objects.defcon import GlyphSet, TFont, TGlyph
from defconQt.util import platformSpecific
@@ -191,9 +192,16 @@ class InspectorWindow(QWidget):
transformLayout.addWidget(scaleXYBox, l, 5)
transformGroup.setLayout(transformLayout)
+ layerSetGroup = QGroupBox("Layers", self)
+ layerSetGroup.setFlat(True)
+ layerSetLayout = QGridLayout(self)
+ layerSetLayout.addWidget(LayerSetList(app.currentFont()), 0, 0)
+ layerSetGroup.setLayout(layerSetLayout)
+
mainLayout = QVBoxLayout()
mainLayout.addWidget(glyphGroup)
mainLayout.addWidget(transformGroup)
+ mainLayout.addWidget(layerSetGroup)
self.setLayout(mainLayout)
def showEvent(self, event):
diff --git a/Lib/defconQt/glyphView.py b/Lib/defconQt/glyphView.py
index c68f188..bd5779f 100644
--- a/Lib/defconQt/glyphView.py
+++ b/Lib/defconQt/glyphView.py
@@ -1,5 +1,6 @@
from enum import Enum
from math import copysign
+from functools import partial
import pickle
from defcon import Anchor, Component
from defconQt import icons_db
@@ -8,7 +9,7 @@ from defconQt.pens.copySelectionPen import CopySelectionPen
from defconQt.util import platformSpecific
from fontTools.misc import bezierTools
from PyQt5.QtCore import *#QFile, QLineF, QObject, QPointF, QRectF, QSize, Qt
-from PyQt5.QtGui import *#QBrush, QColor, QImage, QKeySequence, QPainter, QPainterPath, QPixmap, QPen
+from PyQt5.QtGui import *#QBrush, QColor, QImage, QKeySequence, QPainter, QPainterPath, QPixmap, QPen, QColorDialog
from PyQt5.QtWidgets import *#(QAction, QActionGroup, QApplication, QFileDialog,
#QGraphicsItem, QGraphicsEllipseItem, QGraphicsLineItem, QGraphicsPathItem, QGraphicsRectItem, QGraphicsScene, QGraphicsView,
#QMainWindow, QMenu, QMessageBox, QStyle, QStyleOptionGraphicsItem, QWidget)
@@ -170,13 +171,116 @@ class AddLayerDialog(QDialog):
name = dialog.layerNameEdit.text()
return (name, result)
+
+class GenericSettings(object):
+ def __init__(self, title, parent, callback):
+ #super().__init__()
+ self._menuWidget = QMenu(title, parent)
+ self._actions = {}
+ self._callback = callback
+ for key, item in self._items:
+ self._addAction(key, *item)
+
+ if(self._presets):
+ self._menuWidget.addSeparator()
+ for i, presets in enumerate(self._presets):
+ self._addPresetAction(i, *presets)
+
+ _items = {}
+ _presets = tuple()
+
+ @property
+ def menuWidget(self):
+ return self._menuWidget
+
+ def _addAction(self, key, label, checked):
+ action = self._menuWidget.addAction(label, partial(self._callback, key))
+ action.setCheckable(True)
+ action.setChecked(checked)
+ self._actions[key] = action
+
+ def _addPresetAction(self, index, label, presets, **args):
+ self._menuWidget.addAction(label, partial(self._setPreset, index))
+
+ def _setPreset(self, index):
+ _, data = self._presets[index]
+ for key, value in data.items():
+ action = self._actions[key]
+ action.blockSignals(True)
+ action.setChecked(value)
+ action.blockSignals(False)
+ self._callback()
+
+ def __setattr__(self, name, value):
+ if name.startswith('_'):
+ self.__dict__[name] = value
+ return
+ raise AttributeError('It\'s not allowed to set attributes here.', name)
+
+ def __getattr__(self, name):
+ if name.startswith('_'):
+ raise AttributeError(name)
+ if name not in self._actions:
+ raise AttributeError(name)
+ return self._actions[name].isChecked()
+
+
+
+class DisplayStyleSettings(GenericSettings):
+ _presets = (
+ ('TruFont Default', dict(
+ activeLayerOnTop = True
+ , activeLayerFilled = True
+ , otherLayersFilled = False
+ , activeLayerUseLayerColor = False
+ , otherLayerUseLayerColor = True
+ , drawOtherLayers = True
+ ))
+ , ('Layer Fonts', dict(
+ activeLayerOnTop = False
+ , activeLayerFilled = True
+ , otherLayersFilled = True
+ , activeLayerUseLayerColor = True
+ , otherLayerUseLayerColor = True
+ , drawOtherLayers = True
+ ))
+ )
+
+ _items = (
+ ('activeLayerOnTop', ('Active Layer on Top', True))
+ , ('activeLayerFilled', ('Active Layer Filled', True))
+ , ('activeLayerUseLayerColor', ('Active Layer use Custom Color', False))
+ , ('otherLayersFilled', ('Other Layers Filled', False))
+ , ('otherLayerUseLayerColor', ('Other Layers use Custom Color', True))
+ , ('drawOtherLayers', ('Show Other Layers', True))
+ )
+
+
class MainGfxWindow(QMainWindow):
def __init__(self, glyph, parent=None):
super(MainGfxWindow, self).__init__(parent)
self.setAttribute(Qt.WA_DeleteOnClose)
self.setAttribute(Qt.WA_KeyCompression)
- self.view = GlyphView(glyph, self)
+ self.view = None
+
+ self._layerSet = layerSet = glyph.layerSet
+
+ self._layerSetNotifications = []
+
+ layerSet.addObserver(self, '_layerSetLayerAdded', 'LayerSet.LayerAdded')
+ layerSet.addObserver(self, '_layerSetLayerDeleted', 'LayerSet.LayerDeleted')
+ self._layerSetNotifications.append('LayerSet.LayerAdded')
+ self._layerSetNotifications.append('LayerSet.LayerDeleted')
+
+
+ for event in ('LayerSet.LayerChanged', 'LayerSet.DefaultLayerChanged'
+ , 'LayerSet.LayerOrderChanged'):
+ self._layerSetNotifications.append(event)
+ layerSet.addObserver(self, '_layerSetEvents', event)
+
+
+
menuBar = self.menuBar()
fileMenu = QMenu("&File", self)
@@ -187,27 +291,39 @@ class MainGfxWindow(QMainWindow):
glyphMenu.addAction("&Jump", self.changeGlyph, "J")
menuBar.addMenu(glyphMenu)
- toolBar = QToolBar(self)
+
+ self._displaySettings = DisplayStyleSettings("&Display", self, self._displayChanged)
+ menuBar.addMenu(self._displaySettings.menuWidget)
+
+ self._toolBar = toolBar = QToolBar(self)
toolBar.setMovable(False)
toolBar.setContentsMargins(2, 0, 2, 0)
- selectionToolButton = toolBar.addAction("Selection", self.view.setSceneSelection)
+ selectionToolButton = toolBar.addAction("Selection", self._redirect('view', 'setSceneSelection'))
selectionToolButton.setCheckable(True)
selectionToolButton.setChecked(True)
selectionToolButton.setIcon(QIcon(":/resources/cursor.svg"))
- penToolButton = toolBar.addAction("Pen", self.view.setSceneDrawing)
+ penToolButton = toolBar.addAction("Pen", self._redirect('view', 'setSceneDrawing'))
penToolButton.setCheckable(True)
penToolButton.setIcon(QIcon(":/resources/curve.svg"))
- rulerToolButton = toolBar.addAction("Ruler", self.view.setSceneRuler)
+ rulerToolButton = toolBar.addAction("Ruler", self._redirect('view', 'setSceneRuler'))
rulerToolButton.setCheckable(True)
rulerToolButton.setIcon(QIcon(":/resources/ruler.svg"))
- knifeToolButton = toolBar.addAction("Knife", self.view.setSceneKnife)
+ knifeToolButton = toolBar.addAction("Knife", self._redirect('view', 'setSceneKnife'))
knifeToolButton.setCheckable(True)
knifeToolButton.setIcon(QIcon(":/resources/cut.svg"))
+
# http://www.setnode.com/blog/right-aligning-a-button-in-a-qtoolbar/
spacer = QWidget()
spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
toolBar.addWidget(spacer)
- toolBar.addWidget(self.view.currentLayerBox())
+ self._currentLayerBox = QComboBox(self)
+ self._currentLayerBox.currentIndexChanged.connect(self._changeLayerHandler)
+ toolBar.addWidget(self._currentLayerBox)
+
+ self._layerColorButton = toolBar.addAction(
+ "Layer Color (shift + click to remove the color)",
+ self._chooseLayerColor)
+
toolsGroup = QActionGroup(self)
toolsGroup.addAction(selectionToolButton)
toolsGroup.addAction(penToolButton)
@@ -217,21 +333,177 @@ class MainGfxWindow(QMainWindow):
self.setContextMenuPolicy(Qt.ActionsContextMenu)
createAnchorAction = QAction("Add Anchor…", self)
- createAnchorAction.triggered.connect(self.view.createAnchor)
+ createAnchorAction.triggered.connect(self._redirect('view', 'createAnchor'))
self.addAction(createAnchorAction)
createComponentAction = QAction("Add Component…", self)
- createComponentAction.triggered.connect(self.view.createComponent)
+ createComponentAction.triggered.connect(self._redirect('view', 'createComponent'))
self.addAction(createComponentAction)
- self.setCentralWidget(self.view)
+ for layer in self._layerSet:
+ self._listenToLayer(layer)
+
+ self._changeGlyph(glyph)
+ self._updateComboBox()
+
self.setWindowTitle(glyph.name, glyph.getParent())
self.adjustSize()
+ def _changeGlyph(self, glyph):
+ oldView = self.view
+ if oldView:
+ # Preserve the selected layer (by setting the glyph from that layer)
+ # Todo: If that layer is already in the glyph, it would be a little bit
+ # harder to create it here and may be better or worse. Worse becaue
+ # we'd alter the data without being explicitly asked to do so.
+ # Ask someone who does UX.
+ if oldView.layer is not glyph.layer and glyph.name in oldView.layer:
+ glyph = oldView.layer[glyph.name]
+ oldView.hide()
+ oldView.deleteLater()
+
+ self.view = GlyphView(glyph, self._displaySettings, self)
+ # Preserve the zoom level of the oldView.
+ # TODO: is there more state that we need to carry over to the next
+ # GlyphView? If yes, we should make an interface for a
+ # predecessor -> successor transformation
+ if oldView:
+ self.view.setTransform(oldView.transform())
+
+ self._setlayerColorButtonColor()
+
+ app = QApplication.instance()
+ app.setCurrentGlyph(glyph)
+ self.setWindowTitle(glyph.name, glyph.getParent())
+
+ # switch
+ self.setCentralWidget(self.view)
+
+ def _executeRemoteCommand(self, targetName, commandName, *args, **kwds):
+ """
+ Execute a method named `commandName` on the attribute named `targetName`.
+ This strongly suggest that there is always a known interface at self.{targetName}
+ See MainGfxWindow._redirect
+ """
+ target = getattr(self, targetName)
+ command = getattr(target, commandName)
+ command(*args, **kwds)
+
+ def _redirect(self, target, command):
+ """
+ This creates an indirection to permanently connect an event to a
+ property that may (will) change its identity.
+ """
+ return partial(self._executeRemoteCommand, target, command)
+
+ def _layerSetEvents(self, notification):
+ layerSet = notification.object
+ assert layerSet is self._layerSet
+ self._updateComboBox()
+
+
+ def _layerSetLayerDeleted(self, notification):
+ self._layerSetEvents(notification)
+ self._changeLayerHandler(0)
+
+ def _layerSetLayerAdded(self, notification):
+ self._layerSetEvents(notification)
+ layerName = notification.data['name']
+ layer = self._layerSet[layerName]
+ self.view.layerAdded(layer)
+ self._listenToLayer(layer)
+
+ def _listenToLayer(self, layer, remove=False):
+ if remove:
+ layer.removeObserver(self, 'Layer.ColorChanged')
+ else:
+ layer.addObserver(self, '_layerColorChange', 'Layer.ColorChanged')
+
+ def _chooseLayerColor(self):
+ if QApplication.keyboardModifiers() & Qt.ShiftModifier:
+ # reset to default color, i.e. delete the layer color
+ color = None
+ else:
+ startColor = self.view.getLayerColor() or QColor('limegreen')
+ qColor = QColorDialog.getColor(startColor, self, options=QColorDialog.ShowAlphaChannel)
+ if not qColor.isValid():
+ # cancelled
+ return
+ color = qColor.getRgbF()
+ # will trigger Layer.ColorChanged
+ self.view.layer.color = color
+
+ def _layerColorChange(self, notification):
+ layer = notification.object
+ if layer is self.view.layer:
+ self._setlayerColorButtonColor()
+
+ def _setlayerColorButtonColor(self):
+ color = self.view.getLayerColor()
+ if color is None:
+ icon = QIcon(":/resources/defaultColor.svg")
+ else:
+ # set the layer color to the button
+ pixmap = QPixmap(100, 100)
+ pixmap.fill(color)
+ icon = QIcon(pixmap)
+ self._layerColorButton.setIcon(icon)
+
+ def _updateComboBox(self):
+ comboBox = self._currentLayerBox
+ comboBox.blockSignals(True)
+ comboBox.clear()
+ for layer in self._layerSet:
+ comboBox.addItem(layer.name, layer)
+ if layer == self.view.layer:
+ comboBox.setCurrentText(layer.name)
+ continue
+ comboBox.addItem("New layer...", None)
+ comboBox.blockSignals(False)
+
+ def _setComboboxIndex(self, index):
+ comboBox = self._currentLayerBox
+ comboBox.blockSignals(True)
+ comboBox.setCurrentIndex(index)
+ comboBox.blockSignals(False)
+
+ def _makeNewLayerViaDialog(self):
+ newLayerName, ok = AddLayerDialog.getNewLayerName(self)
+ if ok:
+ # this should cause self._layerSetLayerAdded to be executed
+ self._layerSet.newLayer(newLayerName)
+ return self._layerSet[newLayerName]
+ else:
+ return None
+
+ def _changeLayerHandler(self, newLayerIndex):
+ comboBox = self._currentLayerBox
+ layer = comboBox.itemData(newLayerIndex)
+ if layer is None:
+ layer = self._makeNewLayerViaDialog()
+ if layer is None:
+ # restore comboBox to active index
+ index = self._layerSet.layerOrder.index(self.view.layer.name)
+ self._setComboboxIndex(index)
+ return
+
+ self.view.changeCurrentLayer(layer)
+ self._updateComboBox()
+ self._setlayerColorButtonColor()
+
+ app = QApplication.instance()
+ # setting the layer-glyph here
+ app.setCurrentGlyph(self.view._glyph)
+
def changeGlyph(self):
glyph = self.view._glyph
newGlyph, ok = GotoDialog.getNewGlyph(self, glyph)
if ok and newGlyph is not None:
- self.view.setGlyph(newGlyph)
+ self._changeGlyph(newGlyph)
+ self._updateComboBox()
+
+ def _displayChanged(self, *args):
+ # redraw the view
+ self.view.redraw()
def event(self, event):
if event.type() == QEvent.WindowActivate:
@@ -240,11 +512,15 @@ class MainGfxWindow(QMainWindow):
return super(MainGfxWindow, self).event(event)
def closeEvent(self, event):
- self.view._glyph.removeObserver(self, "Glyph.Changed")
- event.accept()
+ for name in self._layerSetNotifications:
+ self._layerSet.removeObserver(self, name)
- def _glyphChanged(self, notification):
- self.view._glyphChanged(notification)
+ for layer in self._layerSet:
+ self._listenToLayer(layer, remove=True)
+
+ self.view = None
+
+ event.accept()
def setWindowTitle(self, title, font=None):
if font is not None: title = "%s – %s %s" % (title, font.info.familyName, font.info.styleName)
@@ -677,7 +953,6 @@ class ComponentItem(QGraphicsPathItem):
super(ComponentItem, self).__init__(path, parent)
self._component = component
self.setTransform(QTransform(*component.transformation))
- self.setBrush(QBrush(componentFillColor))
self.setFlag(QGraphicsItem.ItemIsMovable)
self.setFlag(QGraphicsItem.ItemIsSelectable)
self.setFlag(QGraphicsItem.ItemSendsGeometryChanges)
@@ -784,7 +1059,7 @@ class PixmapItem(QGraphicsPixmapItem):
return value
class GlyphScene(QGraphicsScene):
- def __init__(self, parent):
+ def __init__(self, parent, sceneAddedItems=None):
super(GlyphScene, self).__init__(parent)
self._editing = False
self._integerPlane = True
@@ -796,6 +1071,10 @@ class GlyphScene(QGraphicsScene):
self._dataForUndo = []
self._dataForRedo = []
+ # if this is an array, the Glyphview will clean up all contents
+ # when redrawing the active layer
+ self._sceneAddedItems = sceneAddedItems
+
font = self.font()
font.setFamily("Roboto Mono")
font.setFixedPitch(True)
@@ -809,6 +1088,12 @@ class GlyphScene(QGraphicsScene):
_glyphObject = property(_get_glyphObject, doc="Get the current glyph in the view.")
+ def _addRegisterItem(self, item):
+ """The parent object will take care of removing these again"""
+ if self._sceneAddedItems is not None:
+ self._sceneAddedItems.append(item)
+ self.addItem(item)
+
def dragEnterEvent(self, event):
if event.mimeData().hasUrls():
event.acceptProposedAction()
@@ -832,7 +1117,7 @@ class GlyphScene(QGraphicsScene):
return
pos = event.scenePos()
newPix = PixmapItem(pos.x(), pos.y(), dragPix)
- self.addItem(newPix)
+ self._addRegisterItem(newPix)
event.acceptProposedAction()
def getItemForPoint(self, point):
@@ -1023,7 +1308,7 @@ class GlyphScene(QGraphicsScene):
else:
lastContour.addPoint((x,y), "line")
item = OnCurvePointItem(x, y, False, lastContour, lastContour[-1], self.getViewScale())
- self.addItem(item)
+ self._addRegisterItem(item)
for _ in range(2):
lineObj = HandleLineItem(0, 0, 0, 0, item)
CPObject = OffCurvePointItem(0, 0, item)
@@ -1038,7 +1323,7 @@ class GlyphScene(QGraphicsScene):
nextC.addPoint((x,y), "move")
item = OnCurvePointItem(x, y, False, self._glyphObject[-1], self._glyphObject[-1][-1], self.getViewScale())
- self.addItem(item)
+ self._addRegisterItem(item)
for _ in range(2):
lineObj = HandleLineItem(0, 0, 0, 0, item)
CPObject = OffCurvePointItem(0, 0, item)
@@ -1341,17 +1626,30 @@ class GlyphScene(QGraphicsScene):
self._cachedIntersections = []
event.accept()
+
class GlyphView(QGraphicsView):
- def __init__(self, glyph, parent=None):
+ def __init__(self, glyph, settings, parent=None):
super(GlyphView, self).__init__(parent)
- self._glyph = glyph
- self._glyph.addObserver(self, "_glyphChanged", "Glyph.Changed")
- self._glyph.layerSet.addObserver(self, "_layersChanged", "LayerSet.Changed")
+
+ # wont change during lifetime
+ self._layerSet = layerSet = glyph.layerSet
+ self._name = glyph.name
+ self._settings = settings
+
+ # we got the individual layers as keys => pathItem
+ # we got string keys => [list of scene items] like 'components'
+ self._sceneItems = {}
+
+ # will change during lifetime
+ self._layer = glyph.layer
+
self._impliedPointSize = 1000
self._pointSize = None
+ # apparently unused code
self._inverseScale = 0.1
self._scale = 10
+
self._noPointSizePadding = 200
self._drawStroke = True
self._showOffCurvePoints = True
@@ -1359,15 +1657,12 @@ class GlyphView(QGraphicsView):
self._showMetricsTitles = True
self.setBackgroundBrush(QBrush(Qt.lightGray))
- self.setScene(GlyphScene(self))
+ self.setScene(GlyphScene(self, self._getSceneItems('scene-added-items')))
font = self.font()
font.setFamily("Roboto Mono")
font.setFixedPitch(True)
self.setFont(font)
- self._currentLayerBox = QComboBox(self)
- self._currentLayerBox.currentIndexChanged.connect(self._currentLayerChanged)
-
self.setTransformationAnchor(QGraphicsView.AnchorUnderMouse)
self.setResizeAnchor(QGraphicsView.AnchorUnderMouse)
#self.setViewportUpdateMode(QGraphicsView.BoundingRectViewportUpdate)
@@ -1380,53 +1675,117 @@ class GlyphView(QGraphicsView):
self.addBackground()
self.addBlues()
self.addHorizontalMetrics()
- self.addOtherLayersOutlines()
- self.addOutlines()
- self.addComponents()
- self.addAnchors()
- self.addPoints()
- def currentLayerBox(self):
- return self._currentLayerBox
+ for layer in layerSet:
+ if self._name not in layer:
+ self._listenToLayer(layer)
+ else:
+ self._listenToGlyph(layer)
+ self._listenToLayerSet()
+
+ self.changeCurrentLayer(self._layer)
+
+
+ @property
+ def _glyph(self):
+ # instead of a getter we could change the glyph each time
+ # self._layer is changed. However, it might be that the glyph
+ # is not ready when the layer is set
+ return self._layer[self._name]
+
+ @property
+ def name(self):
+ return self._name
+
+ @property
+ def layer(self):
+ return self._layer
+
+ @property
+ def defaultWidth(self):
+ defaultLayer = self._layerSet[None]
+ return defaultLayer[self._name].width \
+ if self._name in defaultLayer else 0
+
+ def _listenToLayerSet(self):
+ self._layerSet.addObserver(self, '_layerDeleted', 'LayerSet.LayerWillBeDeleted')
+ self._layerSet.addObserver(self, '_layerGenericVisualChange', 'LayerSet.LayerOrderChanged')
+
+ def _listenToLayer(self, layer, remove=False):
+ if remove:
+ layer.removeObserver(self, 'Layer.GlyphAdded')
+ else:
+ layer.addObserver(self, '_layerGlyphAdded', 'Layer.GlyphAdded')
+
+ def _layerGenericVisualChange(self, notification):
+ self.redraw()
+
+ def _listenToGlyph(self, layer):
+ layer[self._name].addObserver(self, "_glyphChanged", "Glyph.Changed")
+ layer.addObserver(self, '_layerGenericVisualChange', 'Layer.ColorChanged')
+
+ def layerAdded(self, layer):
+ self._listenToLayer(layer)
+
+ def _layerGlyphAdded(self, notification):
+ glyphName = notification.data['name']
+ # the layer will emit this for each new glyph
+ if glyphName != self._name:
+ return
+ layer = notification.object
+ self._listenToLayer(layer, remove=True)
+ self._listenToGlyph(layer)
+ self.redraw()
+
+ def _layerDeleted(self, notification):
+ layerName = notification.data['name']
+ layer = self._layerSet[layerName]
+ self._removeLayerPath(layer)
def _glyphChanged(self, notification):
# TODO: maybe detect sidebearing changes (space center) and then only
# translate elements rather than reconstructing them.
# Also we lose selection when reconstructing, rf does not when changing
# sp.center values.
- self.redrawGlyph()
+ glyph = notification.object
+ layer = glyph.layer
+ if layer is self._layer:
+ self.activeGlyphChanged()
+ else:
+ self.updateLayerPath(layer)
+
+ def redraw(self):
+ self._getSceneItems('scene-added-items', clear=True)
+ self.updateLayerAssets()
+ self.drawAllLayers()
+ self.addAnchors()
+ self.addPoints()
- # TODO: diagnose notifications count
- def _layersChanged(self, notification):
- self.redrawGlyph()
- self.redrawOtherLayers()
+ def updateLayerAssets(self):
+ for item in self._getSceneItems('hMetricLabels'):
+ item.setPos(self._glyph.width, item.y())
- def redrawGlyph(self):
- path = self._glyph.getRepresentation("defconQt.NoComponentsQPainterPath")
+ def activeGlyphChanged(self):
+ # For now, we'll assume not scene._blocked == moving UI points
+ # this will not be the case anymore when drag sidebearings pops up
scene = self.scene()
- scene._outlineItem.setPath(path)
- if not scene._blocked:
- # TODO: also rewind anchors and components
- for item in scene.items():
- if isinstance(item, (OnCurvePointItem, ComponentItem, AnchorItem)):
- scene.removeItem(item)
- elif isinstance(item, VGuidelinesTextItem):
- item.setPos(self._glyph.width, item.y())
+ if scene._blocked:
+ self.updateActiveLayerPath()
+ return
+ self.updateActiveLayer()
- self.addComponents()
- self.addAnchors()
- self.addPoints()
- # For now, we'll assume not scene._blocked == moving UI points
- # this will not be the case anymore when drag sidebearings pops up
- scene._widthItem.setRect(0, -1000, self._glyph.width, 3000)
+ def updateActiveLayer(self):
+ self._getSceneItems('scene-added-items', clear=True)
+ self.updateLayerAssets()
+
+ self.updateActiveLayerPath()
+ self.addComponents()
+ self.addAnchors()
+ self.addPoints()
- def redrawOtherLayers(self):
+ # this is related to addBackground
scene = self.scene()
- for item in scene.items():
- # XXX: discriminate better
- if isinstance(item, QGraphicsPathItem) and item.zValue() == -997:
- scene.removeItem(item)
- self.addOtherLayersOutlines()
+ scene._widthItem.setRect(0, -1000, self._glyph.width, 3000)
def addBackground(self):
scene = self.scene()
@@ -1486,6 +1845,9 @@ class GlyphView(QGraphicsView):
y = roundPosition(position)
item = scene.addLine(-1000, y, 2000, y, QPen(metricsColor))
item.setZValue(-997)
+
+
+ labels = self._getSceneItems('hMetricLabels', clear=True)
# text
if self._showMetricsTitles:# and self._impliedPointSize > 150:
fontSize = 9# * self._inverseScale
@@ -1500,56 +1862,164 @@ class GlyphView(QGraphicsView):
item.setFlag(QGraphicsItem.ItemIgnoresTransformations)
item.setPos(width, y)
item.setZValue(-997)
+ labels.append(item)
scene.addItem(item)
- def addOtherLayersOutlines(self):
- comboBox = self._currentLayerBox
- comboBox.blockSignals(True)
- comboBox.clear()
- scene = self.scene()
- layerSet = self._glyph.layerSet
- for layer in layerSet:
- comboBox.addItem(layer.name, layer)
- if layer == self._glyph.layer:
- comboBox.setCurrentText(layer.name)
+ def getLayerColor(self, layer=None):
+ if layer is None:
+ layer = self._layer
+ if layer.color is not None:
+ return QColor.fromRgbF(*layer.color)
+ return None
+
+
+ def _getDrawingStyleForLayer(self, layer, kind=None):
+ isActive = layer is self._layer
+ isFilled = self._settings.activeLayerFilled \
+ if isActive else self._settings.otherLayersFilled
+ isOutlined = not isFilled
+ useLayerColor = self._settings.activeLayerUseLayerColor \
+ if isActive else self._settings.otherLayerUseLayerColor
+
+ if useLayerColor:
+ brushcolor = self.getLayerColor(layer) or Qt.black
+ pencolor = brushcolor
+ else:
+ # default app colors
+ brushcolor = fillColor if kind != 'component' else componentFillColor
+ pencolor = Qt.black
+
+ if isOutlined:
+ pen = QPen(pencolor)
+ brush = QBrush(Qt.NoBrush)
+ else:
+ pen = QPen(Qt.NoPen)
+ brush = QBrush(brushcolor)
+
+ if isActive and not useLayerColor:
+ # The originally released app did fall back to QT's default
+ # behavior i.e. no pen defined, so I preserve this here
+ if isOutlined: brush = None # like Qt.NoBrush
+ else: pen = None # like Qt.black
+ return (pen, brush)
+
+
+
+ @property
+ def _activeLayerZValue(self):
+ return -995 if not self._settings.activeLayerOnTop else -993
+
+ def drawAllLayers(self):
+ activeLayer = self._layer
+ # all layers before the active layer are -996
+ # the active layer is -995 or -993 if self._settings.activeLayerOnTop == True
+ # all layers after the active layer are -996
+ zValue = -996
+ for layer in reversed(list(self._layerSet)):
+ if self._name not in layer:
continue
- if not self._glyph.name in layer:
+ isActiveLayer = layer is activeLayer
+ if isActiveLayer:
+ zValue = self._activeLayerZValue
+ elif not self._settings.drawOtherLayers:
+ self._removeLayerPath(layer)
continue
- path = layer[self._glyph.name].getRepresentation("defconQt.NoComponentsQPainterPath")
- if layer.color is not None:
- layerColor = QColor.fromRgbF(tuple(layer.color))
- else:
- layerColor = Qt.black
- item = scene.addPath(path, QPen(layerColor))
- item.setZValue(-997)
- comboBox.addItem("New layer...", None)
- comboBox.blockSignals(False)
+ self.drawLayer(layer, zValue)
+ if isActiveLayer:
+ zValue = -994
+
+ def _removeLayerPath(self, layer):
+ scene = self.scene()
+ item = self._sceneItems.get(layer, None)
+ if item is not None:
+ scene.removeItem(item)
+ if layer in self._sceneItems:
+ del self._sceneItems[layer]
+
+ def drawLayer(self, layer, zValue):
+ scene = self.scene()
+ glyph = layer[self._name]
+ self._removeLayerPath(layer)
+
+ isActiveLayer = layer is self._layer
+ if isActiveLayer:
+ representationKey = "defconQt.NoComponentsQPainterPath"
+ else:
+ representationKey = "defconQt.QPainterPath"
+
+ path = glyph.getRepresentation(representationKey)
+ item = QGraphicsPathItem()
+ item.setPath(path)
+
+ pen, brush = self._getDrawingStyleForLayer(layer)
+ if pen: item.setPen(pen)
+ if brush: item.setBrush(brush)
+
+ item.setZValue(zValue)
+
+ self._sceneItems[layer] = item
+ scene.addItem(item)
+ if isActiveLayer:
+ # FIXME: don't like this
+ scene._outlineItem = item
+ self.addComponents()
+ return item
- def addOutlines(self):
+ def updateActiveLayerPath(self):
+ self.updateLayerPath(self._layer, representationKey="defconQt.NoComponentsQPainterPath")
+
+ def updateLayerPath(self, layer, representationKey="defconQt.QPainterPath"):
+ glyph = layer[self._name]
scene = self.scene()
- path = self._glyph.getRepresentation("defconQt.NoComponentsQPainterPath")
- scene._outlineItem = scene.addPath(path, brush=QBrush(fillColor))
- scene._outlineItem.setZValue(-995)
+ path = glyph.getRepresentation(representationKey)
+ self._sceneItems[layer].setPath(path)
+
+ def _getSceneItems(self, key, clear=False):
+ items = self._sceneItems.get(key, None)
+ if items is None:
+ items = []
+ self._sceneItems[key] = items
+ elif clear:
+ scene = self.scene()
+ for item in items:
+ scene.removeItem(item)
+ del items[:]
+ return items
def addComponents(self):
scene = self.scene()
- font = self._glyph.getParent()
- for component in self._glyph.components:
- glyph = font[component.baseGlyph]
- path = glyph.getRepresentation("defconQt.QPainterPath")
+ layer = self._layer
+ glyph = self._glyph
+
+ pen, brush = self._getDrawingStyleForLayer(layer, kind='component')
+ components = self._getSceneItems('components', clear=True)
+ for component in glyph.components:
+ if component.baseGlyph not in layer:
+ continue
+ componentGlyph = layer[component.baseGlyph]
+ path = componentGlyph.getRepresentation("defconQt.QPainterPath")
item = ComponentItem(path, component)
- item.setZValue(-996)
+ if pen: item.setPen(pen)
+ if brush: item.setBrush(brush)
+ item.setZValue(self._activeLayerZValue)
+ components.append(item)
scene.addItem(item)
def addAnchors(self):
scene = self.scene()
+ anchors = self._getSceneItems('anchors', clear=True)
+
for anchor in self._glyph.anchors:
item = AnchorItem(anchor, self.transform().m11())
- item.setZValue(-996)
+ item.setZValue(-992)
+ anchors.append(item)
scene.addItem(item)
def addPoints(self):
scene = self.scene()
+
+ pointItems = self._getSceneItems('points', clear=True)
+
# work out appropriate sizes and
# skip if the glyph is too small
pointSize = self._impliedPointSize
@@ -1582,6 +2052,7 @@ class GlyphView(QGraphicsView):
points.append((x, y))
item = OnCurvePointItem(x, y, onCurve.isSmooth, self._glyph[onCurve.contourIndex],
self._glyph[onCurve.contourIndex][onCurve.pointIndex], scale)
+ pointItems.append(item)
scene.addItem(item)
# off curve
for CP in [onCurve.prevCP, onCurve.nextCP]:
@@ -1637,7 +2108,7 @@ class GlyphView(QGraphicsView):
self._drawTextAtPoint(text, attributes, (posX, posY), 3)
'''
- def createAnchor(self):
+ def createAnchor(self, *args):
scene = self.scene()
pos = scene._rightClickPos
if scene._integerPlane:
@@ -1651,51 +2122,42 @@ class GlyphView(QGraphicsView):
anchor.name = newAnchorName
self._glyph.appendAnchor(anchor)
- def createComponent(self):
+ def createComponent(self, *args):
newGlyph, ok = AddComponentDialog.getNewGlyph(self, self._glyph)
if ok and newGlyph is not None:
component = Component()
component.baseGlyph = newGlyph.name
self._glyph.appendComponent(component)
- def _currentLayerChanged(self, newLayerIndex):
- comboBox = self.sender()
- newLayer = comboBox.itemData(newLayerIndex)
- if newLayer is None:
- # add a new layer
- newLayerName, ok = AddLayerDialog.getNewLayerName(self)
- if ok:
- self._glyph.layerSet.newLayer(newLayerName)
- self._currentLayerBox.blockSignals(True)
- self._currentLayerBox.setCurrentText(newLayerName)
- self._currentLayerBox.blockSignals(False)
- newLayer = self._glyph.layerSet[newLayerName]
- else:
- return
- if not self._glyph.name in newLayer:
- newLayer.newGlyph(self._glyph.name)
- # TODO: generalize this out, can’t use newStandardGlyph unfortunately
- newLayer[self._glyph.name].width = self._glyph.width
- newLayer[self._glyph.name].template = True
- newGlyph = newLayer[self._glyph.name]
- self.setGlyph(newGlyph)
-
- def setGlyph(self, glyph):
+ def _makeLayerGlyph(self, layer):
+ name = self._name
+ layer.newGlyph(name)
+ glyph = layer[name]
+ # TODO: generalize this out, can’t use newStandardGlyph unfortunately
+ glyph.width = self.defaultWidth
+
+ # This prevents the empty glyph from being saved.
+ # TGlyph sets it to False when the glyph is marked as dirty
+ glyph.template = True
+ return glyph
+
+ def changeCurrentLayer(self, layer):
+ name = self._name
+
+ # set current layer
+ self._layer = layer
+
+ if name in layer:
+ # please redraw
+ self.redraw()
+ else:
+ # no need to redraw, will happen within _layerGlyphAdded
+ self._makeLayerGlyph(layer)
+
+ # TODO: Undo data does not keep track of different layers
scene = self.scene()
- self._glyph.removeObserver(self, "Glyph.Changed")
- self._glyph.layerSet.removeObserver(self, "LayerSet.Changed")
- # TODO: consider creating a new scene instead of zeroing things out
- # manually
scene._dataForUndo = []
scene._dataForRedo = []
- self._glyph = glyph
- app = QApplication.instance()
- app.setCurrentGlyph(glyph)
- self._glyph.addObserver(self, "_glyphChanged", "Glyph.Changed")
- self._glyph.layerSet.addObserver(self, "_layersChanged", "LayerSet.Changed")
- self.parent().setWindowTitle(self._glyph.name, self._glyph.getParent())
- self.redrawGlyph()
- self.redrawOtherLayers()
def showEvent(self, event):
super(GlyphView, self).showEvent(event)
diff --git a/Lib/defconQt/icons_db.py b/Lib/defconQt/icons_db.py
index f9475a6..ac51125 100644
--- a/Lib/defconQt/icons_db.py
+++ b/Lib/defconQt/icons_db.py
@@ -2,13 +2,306 @@
# Resource object code
#
-# Created by: The Resource Compiler for PyQt5 (Qt v5.3.0)
+# Created by: The Resource Compiler for PyQt5 (Qt v5.4.1)
#
# WARNING! All changes made in this file will be lost!
from PyQt5 import QtCore
qt_resource_data = b"\
+\x00\x00\x03\x30\
+\x3c\
+\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\
+\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x55\x54\x46\
+\x2d\x38\x22\x20\x73\x74\x61\x6e\x64\x61\x6c\x6f\x6e\x65\x3d\x22\
+\x6e\x6f\x22\x3f\x3e\x3c\x73\x76\x67\x20\x78\x6d\x6c\x6e\x73\x3a\
+\x72\x64\x66\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\
+\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\x39\x39\x2f\x30\x32\x2f\x32\
+\x32\x2d\x72\x64\x66\x2d\x73\x79\x6e\x74\x61\x78\x2d\x6e\x73\x23\
+\x22\x20\x78\x6d\x6c\x6e\x73\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\
+\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x32\x30\x30\x30\x2f\
+\x73\x76\x67\x22\x20\x78\x6d\x6c\x6e\x73\x3a\x6f\x73\x62\x3d\x22\
+\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x6f\x70\x65\x6e\x73\
+\x77\x61\x74\x63\x68\x62\x6f\x6f\x6b\x2e\x6f\x72\x67\x2f\x75\x72\
+\x69\x2f\x32\x30\x30\x39\x2f\x6f\x73\x62\x22\x20\x68\x65\x69\x67\
+\x68\x74\x3d\x22\x32\x34\x22\x20\x77\x69\x64\x74\x68\x3d\x22\x32\
+\x34\x22\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\x31\x22\
+\x20\x78\x6d\x6c\x6e\x73\x3a\x63\x63\x3d\x22\x68\x74\x74\x70\x3a\
+\x2f\x2f\x63\x72\x65\x61\x74\x69\x76\x65\x63\x6f\x6d\x6d\x6f\x6e\
+\x73\x2e\x6f\x72\x67\x2f\x6e\x73\x23\x22\x20\x76\x69\x65\x77\x42\
+\x6f\x78\x3d\x22\x30\x20\x30\x20\x32\x34\x20\x32\x34\x22\x20\x78\
+\x6d\x6c\x6e\x73\x3a\x64\x63\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\
+\x70\x75\x72\x6c\x2e\x6f\x72\x67\x2f\x64\x63\x2f\x65\x6c\x65\x6d\
+\x65\x6e\x74\x73\x2f\x31\x2e\x31\x2f\x22\x3e\x3c\x70\x61\x74\x68\
+\x20\x64\x3d\x22\x6d\x31\x34\x2e\x34\x36\x20\x32\x31\x2e\x38\x34\
+\x34\x63\x2d\x2e\x31\x39\x31\x32\x37\x2e\x30\x39\x35\x36\x33\x2d\
+\x2e\x33\x35\x38\x36\x33\x2e\x31\x34\x33\x34\x35\x2d\x2e\x35\x32\
+\x35\x39\x39\x2e\x31\x34\x33\x34\x35\x2d\x2e\x33\x33\x34\x37\x32\
+\x20\x30\x2d\x2e\x36\x34\x35\x35\x33\x2d\x2e\x32\x33\x39\x30\x38\
+\x2d\x2e\x38\x33\x36\x38\x30\x2d\x2e\x36\x36\x39\x34\x34\x6c\x2d\
+\x32\x2e\x33\x34\x33\x2d\x35\x2e\x30\x34\x34\x37\x2d\x32\x2e\x35\
+\x31\x30\x34\x20\x32\x2e\x39\x31\x36\x38\x63\x2d\x2e\x32\x33\x39\
+\x30\x38\x2e\x32\x36\x32\x39\x39\x2d\x2e\x35\x32\x35\x39\x39\x2e\
+\x34\x30\x36\x34\x34\x2d\x2e\x38\x38\x34\x36\x31\x2e\x34\x30\x36\
+\x34\x34\x2d\x2e\x35\x30\x32\x30\x38\x20\x30\x2d\x2e\x39\x30\x38\
+\x35\x32\x2d\x2e\x33\x35\x38\x36\x33\x2d\x2e\x39\x30\x38\x35\x32\
+\x2d\x31\x2e\x30\x30\x34\x32\x76\x2d\x31\x35\x2e\x35\x38\x38\x63\
+\x30\x2d\x2e\x36\x32\x31\x36\x32\x2e\x34\x35\x34\x32\x36\x2d\x31\
+\x2e\x30\x35\x32\x20\x31\x2e\x30\x30\x34\x32\x2d\x31\x2e\x30\x35\
+\x32\x2e\x33\x35\x38\x36\x33\x20\x30\x20\x2e\x36\x36\x39\x34\x34\
+\x2e\x31\x34\x33\x34\x35\x2e\x38\x38\x34\x36\x31\x2e\x33\x38\x32\
+\x35\x34\x6c\x39\x2e\x39\x37\x30\x37\x20\x31\x30\x2e\x37\x38\x34\
+\x63\x2e\x32\x38\x36\x39\x30\x2e\x33\x33\x34\x37\x32\x2e\x35\x32\
+\x35\x39\x39\x2e\x36\x39\x33\x33\x35\x2e\x35\x32\x35\x39\x39\x20\
+\x31\x2e\x31\x32\x33\x37\x20\x30\x20\x2e\x34\x35\x34\x32\x36\x2d\
+\x2e\x33\x33\x34\x37\x32\x2e\x38\x31\x32\x38\x39\x2d\x2e\x39\x30\
+\x38\x35\x32\x2e\x38\x31\x32\x38\x39\x68\x2d\x33\x2e\x35\x38\x36\
+\x33\x6c\x32\x2e\x31\x32\x37\x39\x20\x34\x2e\x37\x31\x63\x2e\x30\
+\x39\x35\x36\x33\x2e\x32\x31\x35\x31\x38\x2e\x31\x34\x33\x34\x35\
+\x2e\x33\x38\x32\x35\x34\x2e\x31\x34\x33\x34\x35\x2e\x36\x32\x31\
+\x36\x32\x20\x30\x20\x2e\x33\x31\x30\x38\x31\x2d\x2e\x32\x36\x32\
+\x39\x39\x2e\x35\x39\x37\x37\x31\x2d\x2e\x35\x39\x37\x37\x31\x2e\
+\x37\x34\x31\x31\x36\x7a\x22\x2f\x3e\x3c\x2f\x73\x76\x67\x3e\
+\x00\x00\x03\x26\
+\x3c\
+\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\
+\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x55\x54\x46\
+\x2d\x38\x22\x3f\x3e\x3c\x21\x44\x4f\x43\x54\x59\x50\x45\x20\x73\
+\x76\x67\x20\x50\x55\x42\x4c\x49\x43\x20\x22\x2d\x2f\x2f\x57\x33\
+\x43\x2f\x2f\x44\x54\x44\x20\x53\x56\x47\x20\x31\x2e\x31\x2f\x2f\
+\x45\x4e\x22\x20\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\
+\x77\x33\x2e\x6f\x72\x67\x2f\x47\x72\x61\x70\x68\x69\x63\x73\x2f\
+\x53\x56\x47\x2f\x31\x2e\x31\x2f\x44\x54\x44\x2f\x73\x76\x67\x31\
+\x31\x2e\x64\x74\x64\x22\x3e\x3c\x73\x76\x67\x20\x78\x6d\x6c\x6e\
+\x73\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\
+\x2e\x6f\x72\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\x67\x22\x20\x78\
+\x6d\x6c\x6e\x73\x3a\x78\x6c\x69\x6e\x6b\x3d\x22\x68\x74\x74\x70\
+\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\
+\x39\x39\x2f\x78\x6c\x69\x6e\x6b\x22\x20\x76\x65\x72\x73\x69\x6f\
+\x6e\x3d\x22\x31\x2e\x31\x22\x20\x77\x69\x64\x74\x68\x3d\x22\x32\
+\x34\x22\x20\x68\x65\x69\x67\x68\x74\x3d\x22\x32\x34\x22\x20\x76\
+\x69\x65\x77\x42\x6f\x78\x3d\x22\x30\x20\x30\x20\x32\x34\x20\x32\
+\x34\x22\x3e\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\x4d\x31\x39\x2c\
+\x33\x4c\x31\x33\x2c\x39\x4c\x31\x35\x2c\x31\x31\x4c\x32\x32\x2c\
+\x34\x56\x33\x4d\x31\x32\x2c\x31\x32\x2e\x35\x41\x30\x2e\x35\x2c\
+\x30\x2e\x35\x20\x30\x20\x30\x2c\x31\x20\x31\x31\x2e\x35\x2c\x31\
+\x32\x41\x30\x2e\x35\x2c\x30\x2e\x35\x20\x30\x20\x30\x2c\x31\x20\
+\x31\x32\x2c\x31\x31\x2e\x35\x41\x30\x2e\x35\x2c\x30\x2e\x35\x20\
+\x30\x20\x30\x2c\x31\x20\x31\x32\x2e\x35\x2c\x31\x32\x41\x30\x2e\
+\x35\x2c\x30\x2e\x35\x20\x30\x20\x30\x2c\x31\x20\x31\x32\x2c\x31\
+\x32\x2e\x35\x4d\x36\x2c\x32\x30\x41\x32\x2c\x32\x20\x30\x20\x30\
+\x2c\x31\x20\x34\x2c\x31\x38\x43\x34\x2c\x31\x36\x2e\x38\x39\x20\
+\x34\x2e\x39\x2c\x31\x36\x20\x36\x2c\x31\x36\x41\x32\x2c\x32\x20\
+\x30\x20\x30\x2c\x31\x20\x38\x2c\x31\x38\x43\x38\x2c\x31\x39\x2e\
+\x31\x31\x20\x37\x2e\x31\x2c\x32\x30\x20\x36\x2c\x32\x30\x4d\x36\
+\x2c\x38\x41\x32\x2c\x32\x20\x30\x20\x30\x2c\x31\x20\x34\x2c\x36\
+\x43\x34\x2c\x34\x2e\x38\x39\x20\x34\x2e\x39\x2c\x34\x20\x36\x2c\
+\x34\x41\x32\x2c\x32\x20\x30\x20\x30\x2c\x31\x20\x38\x2c\x36\x43\
+\x38\x2c\x37\x2e\x31\x31\x20\x37\x2e\x31\x2c\x38\x20\x36\x2c\x38\
+\x4d\x39\x2e\x36\x34\x2c\x37\x2e\x36\x34\x43\x39\x2e\x38\x37\x2c\
+\x37\x2e\x31\x34\x20\x31\x30\x2c\x36\x2e\x35\x39\x20\x31\x30\x2c\
+\x36\x41\x34\x2c\x34\x20\x30\x20\x30\x2c\x30\x20\x36\x2c\x32\x41\
+\x34\x2c\x34\x20\x30\x20\x30\x2c\x30\x20\x32\x2c\x36\x41\x34\x2c\
+\x34\x20\x30\x20\x30\x2c\x30\x20\x36\x2c\x31\x30\x43\x36\x2e\x35\
+\x39\x2c\x31\x30\x20\x37\x2e\x31\x34\x2c\x39\x2e\x38\x37\x20\x37\
+\x2e\x36\x34\x2c\x39\x2e\x36\x34\x4c\x31\x30\x2c\x31\x32\x4c\x37\
+\x2e\x36\x34\x2c\x31\x34\x2e\x33\x36\x43\x37\x2e\x31\x34\x2c\x31\
+\x34\x2e\x31\x33\x20\x36\x2e\x35\x39\x2c\x31\x34\x20\x36\x2c\x31\
+\x34\x41\x34\x2c\x34\x20\x30\x20\x30\x2c\x30\x20\x32\x2c\x31\x38\
+\x41\x34\x2c\x34\x20\x30\x20\x30\x2c\x30\x20\x36\x2c\x32\x32\x41\
+\x34\x2c\x34\x20\x30\x20\x30\x2c\x30\x20\x31\x30\x2c\x31\x38\x43\
+\x31\x30\x2c\x31\x37\x2e\x34\x31\x20\x39\x2e\x38\x37\x2c\x31\x36\
+\x2e\x38\x36\x20\x39\x2e\x36\x34\x2c\x31\x36\x2e\x33\x36\x4c\x31\
+\x32\x2c\x31\x34\x4c\x31\x39\x2c\x32\x31\x48\x32\x32\x56\x32\x30\
+\x4c\x39\x2e\x36\x34\x2c\x37\x2e\x36\x34\x5a\x22\x20\x2f\x3e\x3c\
+\x2f\x73\x76\x67\x3e\
+\x00\x00\x02\x5b\
+\x3c\
+\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\
+\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x55\x54\x46\
+\x2d\x38\x22\x3f\x3e\x3c\x21\x44\x4f\x43\x54\x59\x50\x45\x20\x73\
+\x76\x67\x20\x50\x55\x42\x4c\x49\x43\x20\x22\x2d\x2f\x2f\x57\x33\
+\x43\x2f\x2f\x44\x54\x44\x20\x53\x56\x47\x20\x31\x2e\x31\x2f\x2f\
+\x45\x4e\x22\x20\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\
+\x77\x33\x2e\x6f\x72\x67\x2f\x47\x72\x61\x70\x68\x69\x63\x73\x2f\
+\x53\x56\x47\x2f\x31\x2e\x31\x2f\x44\x54\x44\x2f\x73\x76\x67\x31\
+\x31\x2e\x64\x74\x64\x22\x3e\x3c\x73\x76\x67\x20\x78\x6d\x6c\x6e\
+\x73\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\
+\x2e\x6f\x72\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\x67\x22\x20\x78\
+\x6d\x6c\x6e\x73\x3a\x78\x6c\x69\x6e\x6b\x3d\x22\x68\x74\x74\x70\
+\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\
+\x39\x39\x2f\x78\x6c\x69\x6e\x6b\x22\x20\x76\x65\x72\x73\x69\x6f\
+\x6e\x3d\x22\x31\x2e\x31\x22\x20\x77\x69\x64\x74\x68\x3d\x22\x32\
+\x34\x22\x20\x68\x65\x69\x67\x68\x74\x3d\x22\x32\x34\x22\x20\x76\
+\x69\x65\x77\x42\x6f\x78\x3d\x22\x30\x20\x30\x20\x32\x34\x20\x32\
+\x34\x22\x3e\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\x4d\x31\x2e\x33\
+\x39\x2c\x31\x38\x2e\x33\x36\x4c\x33\x2e\x31\x36\x2c\x31\x36\x2e\
+\x36\x4c\x34\x2e\x35\x38\x2c\x31\x38\x4c\x35\x2e\x36\x34\x2c\x31\
+\x36\x2e\x39\x35\x4c\x34\x2e\x32\x32\x2c\x31\x35\x2e\x35\x34\x4c\
+\x35\x2e\x36\x34\x2c\x31\x34\x2e\x31\x32\x4c\x38\x2e\x31\x31\x2c\
+\x31\x36\x2e\x36\x4c\x39\x2e\x31\x37\x2c\x31\x35\x2e\x35\x34\x4c\
+\x36\x2e\x37\x2c\x31\x33\x2e\x30\x36\x4c\x38\x2e\x31\x31\x2c\x31\
+\x31\x2e\x36\x35\x4c\x39\x2e\x35\x33\x2c\x31\x33\x2e\x30\x36\x4c\
+\x31\x30\x2e\x35\x39\x2c\x31\x32\x4c\x39\x2e\x31\x37\x2c\x31\x30\
+\x2e\x35\x39\x4c\x31\x30\x2e\x35\x39\x2c\x39\x2e\x31\x37\x4c\x31\
+\x33\x2e\x30\x36\x2c\x31\x31\x2e\x36\x35\x4c\x31\x34\x2e\x31\x32\
+\x2c\x31\x30\x2e\x35\x39\x4c\x31\x31\x2e\x36\x35\x2c\x38\x2e\x31\
+\x31\x4c\x31\x33\x2e\x30\x36\x2c\x36\x2e\x37\x4c\x31\x34\x2e\x34\
+\x37\x2c\x38\x2e\x31\x31\x4c\x31\x35\x2e\x35\x34\x2c\x37\x2e\x30\
+\x35\x4c\x31\x34\x2e\x31\x32\x2c\x35\x2e\x36\x34\x4c\x31\x35\x2e\
+\x35\x34\x2c\x34\x2e\x32\x32\x4c\x31\x38\x2c\x36\x2e\x37\x4c\x31\
+\x39\x2e\x30\x37\x2c\x35\x2e\x36\x34\x4c\x31\x36\x2e\x36\x2c\x33\
+\x2e\x31\x36\x4c\x31\x38\x2e\x33\x36\x2c\x31\x2e\x33\x39\x4c\x32\
+\x32\x2e\x36\x31\x2c\x35\x2e\x36\x34\x4c\x35\x2e\x36\x34\x2c\x32\
+\x32\x2e\x36\x31\x4c\x31\x2e\x33\x39\x2c\x31\x38\x2e\x33\x36\x5a\
+\x22\x20\x2f\x3e\x3c\x2f\x73\x76\x67\x3e\
+\x00\x00\x09\x0f\
+\x3c\
+\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\
+\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x55\x54\x46\
+\x2d\x38\x22\x20\x73\x74\x61\x6e\x64\x61\x6c\x6f\x6e\x65\x3d\x22\
+\x6e\x6f\x22\x3f\x3e\x0a\x3c\x73\x76\x67\x0a\x20\x20\x20\x78\x6d\
+\x6c\x6e\x73\x3a\x64\x63\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x70\
+\x75\x72\x6c\x2e\x6f\x72\x67\x2f\x64\x63\x2f\x65\x6c\x65\x6d\x65\
+\x6e\x74\x73\x2f\x31\x2e\x31\x2f\x22\x0a\x20\x20\x20\x78\x6d\x6c\
+\x6e\x73\x3a\x63\x63\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x63\x72\
+\x65\x61\x74\x69\x76\x65\x63\x6f\x6d\x6d\x6f\x6e\x73\x2e\x6f\x72\
+\x67\x2f\x6e\x73\x23\x22\x0a\x20\x20\x20\x78\x6d\x6c\x6e\x73\x3a\
+\x72\x64\x66\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\
+\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\x39\x39\x2f\x30\x32\x2f\x32\
+\x32\x2d\x72\x64\x66\x2d\x73\x79\x6e\x74\x61\x78\x2d\x6e\x73\x23\
+\x22\x0a\x20\x20\x20\x78\x6d\x6c\x6e\x73\x3a\x73\x76\x67\x3d\x22\
+\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\
+\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\x67\x22\x0a\x20\x20\x20\x78\
+\x6d\x6c\x6e\x73\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\
+\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\x67\
+\x22\x0a\x20\x20\x20\x78\x6d\x6c\x6e\x73\x3a\x73\x6f\x64\x69\x70\
+\x6f\x64\x69\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x73\x6f\x64\x69\
+\x70\x6f\x64\x69\x2e\x73\x6f\x75\x72\x63\x65\x66\x6f\x72\x67\x65\
+\x2e\x6e\x65\x74\x2f\x44\x54\x44\x2f\x73\x6f\x64\x69\x70\x6f\x64\
+\x69\x2d\x30\x2e\x64\x74\x64\x22\x0a\x20\x20\x20\x78\x6d\x6c\x6e\
+\x73\x3a\x69\x6e\x6b\x73\x63\x61\x70\x65\x3d\x22\x68\x74\x74\x70\
+\x3a\x2f\x2f\x77\x77\x77\x2e\x69\x6e\x6b\x73\x63\x61\x70\x65\x2e\
+\x6f\x72\x67\x2f\x6e\x61\x6d\x65\x73\x70\x61\x63\x65\x73\x2f\x69\
+\x6e\x6b\x73\x63\x61\x70\x65\x22\x0a\x20\x20\x20\x76\x65\x72\x73\
+\x69\x6f\x6e\x3d\x22\x31\x2e\x31\x22\x0a\x20\x20\x20\x77\x69\x64\
+\x74\x68\x3d\x22\x32\x34\x22\x0a\x20\x20\x20\x68\x65\x69\x67\x68\
+\x74\x3d\x22\x32\x34\x22\x0a\x20\x20\x20\x76\x69\x65\x77\x42\x6f\
+\x78\x3d\x22\x30\x20\x30\x20\x32\x34\x20\x32\x34\x22\x0a\x20\x20\
+\x20\x69\x64\x3d\x22\x73\x76\x67\x32\x22\x0a\x20\x20\x20\x69\x6e\
+\x6b\x73\x63\x61\x70\x65\x3a\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\
+\x30\x2e\x39\x31\x20\x72\x31\x33\x37\x32\x35\x22\x0a\x20\x20\x20\
+\x73\x6f\x64\x69\x70\x6f\x64\x69\x3a\x64\x6f\x63\x6e\x61\x6d\x65\
+\x3d\x22\x64\x65\x66\x61\x75\x6c\x74\x43\x6f\x6c\x6f\x72\x2e\x73\
+\x76\x67\x22\x3e\x0a\x20\x20\x3c\x6d\x65\x74\x61\x64\x61\x74\x61\
+\x0a\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x6d\x65\x74\x61\x64\x61\
+\x74\x61\x31\x30\x22\x3e\x0a\x20\x20\x20\x20\x3c\x72\x64\x66\x3a\
+\x52\x44\x46\x3e\x0a\x20\x20\x20\x20\x20\x20\x3c\x63\x63\x3a\x57\
+\x6f\x72\x6b\x0a\x20\x20\x20\x20\x20\x20\x20\x20\x20\x72\x64\x66\
+\x3a\x61\x62\x6f\x75\x74\x3d\x22\x22\x3e\x0a\x20\x20\x20\x20\x20\
+\x20\x20\x20\x3c\x64\x63\x3a\x66\x6f\x72\x6d\x61\x74\x3e\x69\x6d\
+\x61\x67\x65\x2f\x73\x76\x67\x2b\x78\x6d\x6c\x3c\x2f\x64\x63\x3a\
+\x66\x6f\x72\x6d\x61\x74\x3e\x0a\x20\x20\x20\x20\x20\x20\x20\x20\
+\x3c\x64\x63\x3a\x74\x79\x70\x65\x0a\x20\x20\x20\x20\x20\x20\x20\
+\x20\x20\x20\x20\x72\x64\x66\x3a\x72\x65\x73\x6f\x75\x72\x63\x65\
+\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x70\x75\x72\x6c\x2e\x6f\x72\
+\x67\x2f\x64\x63\x2f\x64\x63\x6d\x69\x74\x79\x70\x65\x2f\x53\x74\
+\x69\x6c\x6c\x49\x6d\x61\x67\x65\x22\x20\x2f\x3e\x0a\x20\x20\x20\
+\x20\x20\x20\x20\x20\x3c\x64\x63\x3a\x74\x69\x74\x6c\x65\x20\x2f\
+\x3e\x0a\x20\x20\x20\x20\x20\x20\x3c\x2f\x63\x63\x3a\x57\x6f\x72\
+\x6b\x3e\x0a\x20\x20\x20\x20\x3c\x2f\x72\x64\x66\x3a\x52\x44\x46\
+\x3e\x0a\x20\x20\x3c\x2f\x6d\x65\x74\x61\x64\x61\x74\x61\x3e\x0a\
+\x20\x20\x3c\x64\x65\x66\x73\x0a\x20\x20\x20\x20\x20\x69\x64\x3d\
+\x22\x64\x65\x66\x73\x38\x22\x20\x2f\x3e\x0a\x20\x20\x3c\x73\x6f\
+\x64\x69\x70\x6f\x64\x69\x3a\x6e\x61\x6d\x65\x64\x76\x69\x65\x77\
+\x0a\x20\x20\x20\x20\x20\x70\x61\x67\x65\x63\x6f\x6c\x6f\x72\x3d\
+\x22\x23\x66\x66\x66\x66\x66\x66\x22\x0a\x20\x20\x20\x20\x20\x62\
+\x6f\x72\x64\x65\x72\x63\x6f\x6c\x6f\x72\x3d\x22\x23\x36\x36\x36\
+\x36\x36\x36\x22\x0a\x20\x20\x20\x20\x20\x62\x6f\x72\x64\x65\x72\
+\x6f\x70\x61\x63\x69\x74\x79\x3d\x22\x31\x22\x0a\x20\x20\x20\x20\
+\x20\x6f\x62\x6a\x65\x63\x74\x74\x6f\x6c\x65\x72\x61\x6e\x63\x65\
+\x3d\x22\x31\x30\x22\x0a\x20\x20\x20\x20\x20\x67\x72\x69\x64\x74\
+\x6f\x6c\x65\x72\x61\x6e\x63\x65\x3d\x22\x31\x30\x22\x0a\x20\x20\
+\x20\x20\x20\x67\x75\x69\x64\x65\x74\x6f\x6c\x65\x72\x61\x6e\x63\
+\x65\x3d\x22\x31\x30\x22\x0a\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\
+\x63\x61\x70\x65\x3a\x70\x61\x67\x65\x6f\x70\x61\x63\x69\x74\x79\
+\x3d\x22\x30\x22\x0a\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\
+\x70\x65\x3a\x70\x61\x67\x65\x73\x68\x61\x64\x6f\x77\x3d\x22\x32\
+\x22\x0a\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\
+\x77\x69\x6e\x64\x6f\x77\x2d\x77\x69\x64\x74\x68\x3d\x22\x31\x39\
+\x32\x30\x22\x0a\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\
+\x65\x3a\x77\x69\x6e\x64\x6f\x77\x2d\x68\x65\x69\x67\x68\x74\x3d\
+\x22\x31\x31\x37\x31\x22\x0a\x20\x20\x20\x20\x20\x69\x64\x3d\x22\
+\x6e\x61\x6d\x65\x64\x76\x69\x65\x77\x36\x22\x0a\x20\x20\x20\x20\
+\x20\x73\x68\x6f\x77\x67\x72\x69\x64\x3d\x22\x66\x61\x6c\x73\x65\
+\x22\x0a\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\
+\x7a\x6f\x6f\x6d\x3d\x22\x31\x33\x2e\x39\x30\x36\x34\x33\x33\x22\
+\x0a\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x63\
+\x78\x3d\x22\x31\x31\x2e\x37\x37\x35\x32\x31\x31\x22\x0a\x20\x20\
+\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x63\x79\x3d\x22\
+\x37\x2e\x32\x34\x32\x33\x32\x31\x34\x22\x0a\x20\x20\x20\x20\x20\
+\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x77\x69\x6e\x64\x6f\x77\x2d\
+\x78\x3d\x22\x30\x22\x0a\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\
+\x61\x70\x65\x3a\x77\x69\x6e\x64\x6f\x77\x2d\x79\x3d\x22\x32\x22\
+\x0a\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\x65\x3a\x77\
+\x69\x6e\x64\x6f\x77\x2d\x6d\x61\x78\x69\x6d\x69\x7a\x65\x64\x3d\
+\x22\x31\x22\x0a\x20\x20\x20\x20\x20\x69\x6e\x6b\x73\x63\x61\x70\
+\x65\x3a\x63\x75\x72\x72\x65\x6e\x74\x2d\x6c\x61\x79\x65\x72\x3d\
+\x22\x73\x76\x67\x32\x22\x20\x2f\x3e\x0a\x20\x20\x3c\x72\x65\x63\
+\x74\x0a\x20\x20\x20\x20\x20\x73\x74\x79\x6c\x65\x3d\x22\x63\x6f\
+\x6c\x6f\x72\x3a\x23\x30\x30\x30\x30\x30\x30\x3b\x64\x69\x73\x70\
+\x6c\x61\x79\x3a\x69\x6e\x6c\x69\x6e\x65\x3b\x6f\x76\x65\x72\x66\
+\x6c\x6f\x77\x3a\x76\x69\x73\x69\x62\x6c\x65\x3b\x76\x69\x73\x69\
+\x62\x69\x6c\x69\x74\x79\x3a\x76\x69\x73\x69\x62\x6c\x65\x3b\x6f\
+\x70\x61\x63\x69\x74\x79\x3a\x31\x3b\x66\x69\x6c\x6c\x3a\x23\x66\
+\x66\x66\x66\x66\x66\x3b\x66\x69\x6c\x6c\x2d\x6f\x70\x61\x63\x69\
+\x74\x79\x3a\x31\x3b\x66\x69\x6c\x6c\x2d\x72\x75\x6c\x65\x3a\x6e\
+\x6f\x6e\x7a\x65\x72\x6f\x3b\x73\x74\x72\x6f\x6b\x65\x3a\x6e\x6f\
+\x6e\x65\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x77\x69\x64\x74\x68\x3a\
+\x31\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6c\x69\x6e\x65\x63\x61\x70\
+\x3a\x72\x6f\x75\x6e\x64\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6c\x69\
+\x6e\x65\x6a\x6f\x69\x6e\x3a\x72\x6f\x75\x6e\x64\x3b\x73\x74\x72\
+\x6f\x6b\x65\x2d\x6d\x69\x74\x65\x72\x6c\x69\x6d\x69\x74\x3a\x34\
+\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x64\x61\x73\x68\x61\x72\x72\x61\
+\x79\x3a\x6e\x6f\x6e\x65\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x64\x61\
+\x73\x68\x6f\x66\x66\x73\x65\x74\x3a\x30\x3b\x73\x74\x72\x6f\x6b\
+\x65\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x3b\x6d\x61\x72\x6b\
+\x65\x72\x3a\x6e\x6f\x6e\x65\x3b\x65\x6e\x61\x62\x6c\x65\x2d\x62\
+\x61\x63\x6b\x67\x72\x6f\x75\x6e\x64\x3a\x61\x63\x63\x75\x6d\x75\
+\x6c\x61\x74\x65\x22\x0a\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x72\
+\x65\x63\x74\x34\x31\x34\x31\x22\x0a\x20\x20\x20\x20\x20\x77\x69\
+\x64\x74\x68\x3d\x22\x32\x34\x22\x0a\x20\x20\x20\x20\x20\x68\x65\
+\x69\x67\x68\x74\x3d\x22\x32\x34\x22\x0a\x20\x20\x20\x20\x20\x78\
+\x3d\x22\x30\x22\x0a\x20\x20\x20\x20\x20\x79\x3d\x22\x30\x22\x20\
+\x2f\x3e\x0a\x20\x20\x3c\x70\x61\x74\x68\x0a\x20\x20\x20\x20\x20\
+\x73\x74\x79\x6c\x65\x3d\x22\x63\x6f\x6c\x6f\x72\x3a\x23\x30\x30\
+\x30\x30\x30\x30\x3b\x64\x69\x73\x70\x6c\x61\x79\x3a\x69\x6e\x6c\
+\x69\x6e\x65\x3b\x6f\x76\x65\x72\x66\x6c\x6f\x77\x3a\x76\x69\x73\
+\x69\x62\x6c\x65\x3b\x76\x69\x73\x69\x62\x69\x6c\x69\x74\x79\x3a\
+\x76\x69\x73\x69\x62\x6c\x65\x3b\x6f\x70\x61\x63\x69\x74\x79\x3a\
+\x31\x3b\x66\x69\x6c\x6c\x3a\x23\x66\x66\x30\x30\x30\x30\x3b\x66\
+\x69\x6c\x6c\x2d\x6f\x70\x61\x63\x69\x74\x79\x3a\x31\x3b\x66\x69\
+\x6c\x6c\x2d\x72\x75\x6c\x65\x3a\x6e\x6f\x6e\x7a\x65\x72\x6f\x3b\
+\x73\x74\x72\x6f\x6b\x65\x3a\x6e\x6f\x6e\x65\x3b\x73\x74\x72\x6f\
+\x6b\x65\x2d\x77\x69\x64\x74\x68\x3a\x31\x3b\x73\x74\x72\x6f\x6b\
+\x65\x2d\x6c\x69\x6e\x65\x63\x61\x70\x3a\x72\x6f\x75\x6e\x64\x3b\
+\x73\x74\x72\x6f\x6b\x65\x2d\x6c\x69\x6e\x65\x6a\x6f\x69\x6e\x3a\
+\x72\x6f\x75\x6e\x64\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6d\x69\x74\
+\x65\x72\x6c\x69\x6d\x69\x74\x3a\x34\x3b\x73\x74\x72\x6f\x6b\x65\
+\x2d\x64\x61\x73\x68\x61\x72\x72\x61\x79\x3a\x6e\x6f\x6e\x65\x3b\
+\x73\x74\x72\x6f\x6b\x65\x2d\x64\x61\x73\x68\x6f\x66\x66\x73\x65\
+\x74\x3a\x30\x3b\x73\x74\x72\x6f\x6b\x65\x2d\x6f\x70\x61\x63\x69\
+\x74\x79\x3a\x31\x3b\x6d\x61\x72\x6b\x65\x72\x3a\x6e\x6f\x6e\x65\
+\x3b\x65\x6e\x61\x62\x6c\x65\x2d\x62\x61\x63\x6b\x67\x72\x6f\x75\
+\x6e\x64\x3a\x61\x63\x63\x75\x6d\x75\x6c\x61\x74\x65\x22\x0a\x20\
+\x20\x20\x20\x20\x64\x3d\x22\x4d\x20\x31\x37\x2e\x35\x34\x36\x38\
+\x37\x35\x20\x30\x20\x4c\x20\x30\x20\x31\x37\x2e\x35\x34\x36\x38\
+\x37\x35\x20\x4c\x20\x30\x20\x32\x34\x20\x4c\x20\x35\x2e\x35\x36\
+\x36\x34\x30\x36\x32\x20\x32\x34\x20\x4c\x20\x32\x34\x20\x35\x2e\
+\x35\x36\x36\x34\x30\x36\x32\x20\x4c\x20\x32\x34\x20\x30\x20\x4c\
+\x20\x31\x37\x2e\x35\x34\x36\x38\x37\x35\x20\x30\x20\x7a\x20\x22\
+\x0a\x20\x20\x20\x20\x20\x69\x64\x3d\x22\x72\x65\x63\x74\x34\x31\
+\x34\x35\x22\x20\x2f\x3e\x0a\x3c\x2f\x73\x76\x67\x3e\x0a\
\x00\x01\x37\x1b\
\x89\
\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\
@@ -4989,152 +5282,6 @@ qt_resource_data = b"\
\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
\xd8\x4b\x00\x01\x06\x00\x70\x70\x21\x6a\x56\x20\xd0\xef\x00\x00\
\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\
-\x00\x00\x02\x5b\
-\x3c\
-\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\
-\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x55\x54\x46\
-\x2d\x38\x22\x3f\x3e\x3c\x21\x44\x4f\x43\x54\x59\x50\x45\x20\x73\
-\x76\x67\x20\x50\x55\x42\x4c\x49\x43\x20\x22\x2d\x2f\x2f\x57\x33\
-\x43\x2f\x2f\x44\x54\x44\x20\x53\x56\x47\x20\x31\x2e\x31\x2f\x2f\
-\x45\x4e\x22\x20\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\
-\x77\x33\x2e\x6f\x72\x67\x2f\x47\x72\x61\x70\x68\x69\x63\x73\x2f\
-\x53\x56\x47\x2f\x31\x2e\x31\x2f\x44\x54\x44\x2f\x73\x76\x67\x31\
-\x31\x2e\x64\x74\x64\x22\x3e\x3c\x73\x76\x67\x20\x78\x6d\x6c\x6e\
-\x73\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\
-\x2e\x6f\x72\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\x67\x22\x20\x78\
-\x6d\x6c\x6e\x73\x3a\x78\x6c\x69\x6e\x6b\x3d\x22\x68\x74\x74\x70\
-\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\
-\x39\x39\x2f\x78\x6c\x69\x6e\x6b\x22\x20\x76\x65\x72\x73\x69\x6f\
-\x6e\x3d\x22\x31\x2e\x31\x22\x20\x77\x69\x64\x74\x68\x3d\x22\x32\
-\x34\x22\x20\x68\x65\x69\x67\x68\x74\x3d\x22\x32\x34\x22\x20\x76\
-\x69\x65\x77\x42\x6f\x78\x3d\x22\x30\x20\x30\x20\x32\x34\x20\x32\
-\x34\x22\x3e\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\x4d\x31\x2e\x33\
-\x39\x2c\x31\x38\x2e\x33\x36\x4c\x33\x2e\x31\x36\x2c\x31\x36\x2e\
-\x36\x4c\x34\x2e\x35\x38\x2c\x31\x38\x4c\x35\x2e\x36\x34\x2c\x31\
-\x36\x2e\x39\x35\x4c\x34\x2e\x32\x32\x2c\x31\x35\x2e\x35\x34\x4c\
-\x35\x2e\x36\x34\x2c\x31\x34\x2e\x31\x32\x4c\x38\x2e\x31\x31\x2c\
-\x31\x36\x2e\x36\x4c\x39\x2e\x31\x37\x2c\x31\x35\x2e\x35\x34\x4c\
-\x36\x2e\x37\x2c\x31\x33\x2e\x30\x36\x4c\x38\x2e\x31\x31\x2c\x31\
-\x31\x2e\x36\x35\x4c\x39\x2e\x35\x33\x2c\x31\x33\x2e\x30\x36\x4c\
-\x31\x30\x2e\x35\x39\x2c\x31\x32\x4c\x39\x2e\x31\x37\x2c\x31\x30\
-\x2e\x35\x39\x4c\x31\x30\x2e\x35\x39\x2c\x39\x2e\x31\x37\x4c\x31\
-\x33\x2e\x30\x36\x2c\x31\x31\x2e\x36\x35\x4c\x31\x34\x2e\x31\x32\
-\x2c\x31\x30\x2e\x35\x39\x4c\x31\x31\x2e\x36\x35\x2c\x38\x2e\x31\
-\x31\x4c\x31\x33\x2e\x30\x36\x2c\x36\x2e\x37\x4c\x31\x34\x2e\x34\
-\x37\x2c\x38\x2e\x31\x31\x4c\x31\x35\x2e\x35\x34\x2c\x37\x2e\x30\
-\x35\x4c\x31\x34\x2e\x31\x32\x2c\x35\x2e\x36\x34\x4c\x31\x35\x2e\
-\x35\x34\x2c\x34\x2e\x32\x32\x4c\x31\x38\x2c\x36\x2e\x37\x4c\x31\
-\x39\x2e\x30\x37\x2c\x35\x2e\x36\x34\x4c\x31\x36\x2e\x36\x2c\x33\
-\x2e\x31\x36\x4c\x31\x38\x2e\x33\x36\x2c\x31\x2e\x33\x39\x4c\x32\
-\x32\x2e\x36\x31\x2c\x35\x2e\x36\x34\x4c\x35\x2e\x36\x34\x2c\x32\
-\x32\x2e\x36\x31\x4c\x31\x2e\x33\x39\x2c\x31\x38\x2e\x33\x36\x5a\
-\x22\x20\x2f\x3e\x3c\x2f\x73\x76\x67\x3e\
-\x00\x00\x03\x26\
-\x3c\
-\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\
-\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x55\x54\x46\
-\x2d\x38\x22\x3f\x3e\x3c\x21\x44\x4f\x43\x54\x59\x50\x45\x20\x73\
-\x76\x67\x20\x50\x55\x42\x4c\x49\x43\x20\x22\x2d\x2f\x2f\x57\x33\
-\x43\x2f\x2f\x44\x54\x44\x20\x53\x56\x47\x20\x31\x2e\x31\x2f\x2f\
-\x45\x4e\x22\x20\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\
-\x77\x33\x2e\x6f\x72\x67\x2f\x47\x72\x61\x70\x68\x69\x63\x73\x2f\
-\x53\x56\x47\x2f\x31\x2e\x31\x2f\x44\x54\x44\x2f\x73\x76\x67\x31\
-\x31\x2e\x64\x74\x64\x22\x3e\x3c\x73\x76\x67\x20\x78\x6d\x6c\x6e\
-\x73\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\
-\x2e\x6f\x72\x67\x2f\x32\x30\x30\x30\x2f\x73\x76\x67\x22\x20\x78\
-\x6d\x6c\x6e\x73\x3a\x78\x6c\x69\x6e\x6b\x3d\x22\x68\x74\x74\x70\
-\x3a\x2f\x2f\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\
-\x39\x39\x2f\x78\x6c\x69\x6e\x6b\x22\x20\x76\x65\x72\x73\x69\x6f\
-\x6e\x3d\x22\x31\x2e\x31\x22\x20\x77\x69\x64\x74\x68\x3d\x22\x32\
-\x34\x22\x20\x68\x65\x69\x67\x68\x74\x3d\x22\x32\x34\x22\x20\x76\
-\x69\x65\x77\x42\x6f\x78\x3d\x22\x30\x20\x30\x20\x32\x34\x20\x32\
-\x34\x22\x3e\x3c\x70\x61\x74\x68\x20\x64\x3d\x22\x4d\x31\x39\x2c\
-\x33\x4c\x31\x33\x2c\x39\x4c\x31\x35\x2c\x31\x31\x4c\x32\x32\x2c\
-\x34\x56\x33\x4d\x31\x32\x2c\x31\x32\x2e\x35\x41\x30\x2e\x35\x2c\
-\x30\x2e\x35\x20\x30\x20\x30\x2c\x31\x20\x31\x31\x2e\x35\x2c\x31\
-\x32\x41\x30\x2e\x35\x2c\x30\x2e\x35\x20\x30\x20\x30\x2c\x31\x20\
-\x31\x32\x2c\x31\x31\x2e\x35\x41\x30\x2e\x35\x2c\x30\x2e\x35\x20\
-\x30\x20\x30\x2c\x31\x20\x31\x32\x2e\x35\x2c\x31\x32\x41\x30\x2e\
-\x35\x2c\x30\x2e\x35\x20\x30\x20\x30\x2c\x31\x20\x31\x32\x2c\x31\
-\x32\x2e\x35\x4d\x36\x2c\x32\x30\x41\x32\x2c\x32\x20\x30\x20\x30\
-\x2c\x31\x20\x34\x2c\x31\x38\x43\x34\x2c\x31\x36\x2e\x38\x39\x20\
-\x34\x2e\x39\x2c\x31\x36\x20\x36\x2c\x31\x36\x41\x32\x2c\x32\x20\
-\x30\x20\x30\x2c\x31\x20\x38\x2c\x31\x38\x43\x38\x2c\x31\x39\x2e\
-\x31\x31\x20\x37\x2e\x31\x2c\x32\x30\x20\x36\x2c\x32\x30\x4d\x36\
-\x2c\x38\x41\x32\x2c\x32\x20\x30\x20\x30\x2c\x31\x20\x34\x2c\x36\
-\x43\x34\x2c\x34\x2e\x38\x39\x20\x34\x2e\x39\x2c\x34\x20\x36\x2c\
-\x34\x41\x32\x2c\x32\x20\x30\x20\x30\x2c\x31\x20\x38\x2c\x36\x43\
-\x38\x2c\x37\x2e\x31\x31\x20\x37\x2e\x31\x2c\x38\x20\x36\x2c\x38\
-\x4d\x39\x2e\x36\x34\x2c\x37\x2e\x36\x34\x43\x39\x2e\x38\x37\x2c\
-\x37\x2e\x31\x34\x20\x31\x30\x2c\x36\x2e\x35\x39\x20\x31\x30\x2c\
-\x36\x41\x34\x2c\x34\x20\x30\x20\x30\x2c\x30\x20\x36\x2c\x32\x41\
-\x34\x2c\x34\x20\x30\x20\x30\x2c\x30\x20\x32\x2c\x36\x41\x34\x2c\
-\x34\x20\x30\x20\x30\x2c\x30\x20\x36\x2c\x31\x30\x43\x36\x2e\x35\
-\x39\x2c\x31\x30\x20\x37\x2e\x31\x34\x2c\x39\x2e\x38\x37\x20\x37\
-\x2e\x36\x34\x2c\x39\x2e\x36\x34\x4c\x31\x30\x2c\x31\x32\x4c\x37\
-\x2e\x36\x34\x2c\x31\x34\x2e\x33\x36\x43\x37\x2e\x31\x34\x2c\x31\
-\x34\x2e\x31\x33\x20\x36\x2e\x35\x39\x2c\x31\x34\x20\x36\x2c\x31\
-\x34\x41\x34\x2c\x34\x20\x30\x20\x30\x2c\x30\x20\x32\x2c\x31\x38\
-\x41\x34\x2c\x34\x20\x30\x20\x30\x2c\x30\x20\x36\x2c\x32\x32\x41\
-\x34\x2c\x34\x20\x30\x20\x30\x2c\x30\x20\x31\x30\x2c\x31\x38\x43\
-\x31\x30\x2c\x31\x37\x2e\x34\x31\x20\x39\x2e\x38\x37\x2c\x31\x36\
-\x2e\x38\x36\x20\x39\x2e\x36\x34\x2c\x31\x36\x2e\x33\x36\x4c\x31\
-\x32\x2c\x31\x34\x4c\x31\x39\x2c\x32\x31\x48\x32\x32\x56\x32\x30\
-\x4c\x39\x2e\x36\x34\x2c\x37\x2e\x36\x34\x5a\x22\x20\x2f\x3e\x3c\
-\x2f\x73\x76\x67\x3e\
-\x00\x00\x03\x30\
-\x3c\
-\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\
-\x30\x22\x20\x65\x6e\x63\x6f\x64\x69\x6e\x67\x3d\x22\x55\x54\x46\
-\x2d\x38\x22\x20\x73\x74\x61\x6e\x64\x61\x6c\x6f\x6e\x65\x3d\x22\
-\x6e\x6f\x22\x3f\x3e\x3c\x73\x76\x67\x20\x78\x6d\x6c\x6e\x73\x3a\
-\x72\x64\x66\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\
-\x77\x33\x2e\x6f\x72\x67\x2f\x31\x39\x39\x39\x2f\x30\x32\x2f\x32\
-\x32\x2d\x72\x64\x66\x2d\x73\x79\x6e\x74\x61\x78\x2d\x6e\x73\x23\
-\x22\x20\x78\x6d\x6c\x6e\x73\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\
-\x77\x77\x77\x2e\x77\x33\x2e\x6f\x72\x67\x2f\x32\x30\x30\x30\x2f\
-\x73\x76\x67\x22\x20\x78\x6d\x6c\x6e\x73\x3a\x6f\x73\x62\x3d\x22\
-\x68\x74\x74\x70\x3a\x2f\x2f\x77\x77\x77\x2e\x6f\x70\x65\x6e\x73\
-\x77\x61\x74\x63\x68\x62\x6f\x6f\x6b\x2e\x6f\x72\x67\x2f\x75\x72\
-\x69\x2f\x32\x30\x30\x39\x2f\x6f\x73\x62\x22\x20\x68\x65\x69\x67\
-\x68\x74\x3d\x22\x32\x34\x22\x20\x77\x69\x64\x74\x68\x3d\x22\x32\
-\x34\x22\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\x31\x22\
-\x20\x78\x6d\x6c\x6e\x73\x3a\x63\x63\x3d\x22\x68\x74\x74\x70\x3a\
-\x2f\x2f\x63\x72\x65\x61\x74\x69\x76\x65\x63\x6f\x6d\x6d\x6f\x6e\
-\x73\x2e\x6f\x72\x67\x2f\x6e\x73\x23\x22\x20\x76\x69\x65\x77\x42\
-\x6f\x78\x3d\x22\x30\x20\x30\x20\x32\x34\x20\x32\x34\x22\x20\x78\
-\x6d\x6c\x6e\x73\x3a\x64\x63\x3d\x22\x68\x74\x74\x70\x3a\x2f\x2f\
-\x70\x75\x72\x6c\x2e\x6f\x72\x67\x2f\x64\x63\x2f\x65\x6c\x65\x6d\
-\x65\x6e\x74\x73\x2f\x31\x2e\x31\x2f\x22\x3e\x3c\x70\x61\x74\x68\
-\x20\x64\x3d\x22\x6d\x31\x34\x2e\x34\x36\x20\x32\x31\x2e\x38\x34\
-\x34\x63\x2d\x2e\x31\x39\x31\x32\x37\x2e\x30\x39\x35\x36\x33\x2d\
-\x2e\x33\x35\x38\x36\x33\x2e\x31\x34\x33\x34\x35\x2d\x2e\x35\x32\
-\x35\x39\x39\x2e\x31\x34\x33\x34\x35\x2d\x2e\x33\x33\x34\x37\x32\
-\x20\x30\x2d\x2e\x36\x34\x35\x35\x33\x2d\x2e\x32\x33\x39\x30\x38\
-\x2d\x2e\x38\x33\x36\x38\x30\x2d\x2e\x36\x36\x39\x34\x34\x6c\x2d\
-\x32\x2e\x33\x34\x33\x2d\x35\x2e\x30\x34\x34\x37\x2d\x32\x2e\x35\
-\x31\x30\x34\x20\x32\x2e\x39\x31\x36\x38\x63\x2d\x2e\x32\x33\x39\
-\x30\x38\x2e\x32\x36\x32\x39\x39\x2d\x2e\x35\x32\x35\x39\x39\x2e\
-\x34\x30\x36\x34\x34\x2d\x2e\x38\x38\x34\x36\x31\x2e\x34\x30\x36\
-\x34\x34\x2d\x2e\x35\x30\x32\x30\x38\x20\x30\x2d\x2e\x39\x30\x38\
-\x35\x32\x2d\x2e\x33\x35\x38\x36\x33\x2d\x2e\x39\x30\x38\x35\x32\
-\x2d\x31\x2e\x30\x30\x34\x32\x76\x2d\x31\x35\x2e\x35\x38\x38\x63\
-\x30\x2d\x2e\x36\x32\x31\x36\x32\x2e\x34\x35\x34\x32\x36\x2d\x31\
-\x2e\x30\x35\x32\x20\x31\x2e\x30\x30\x34\x32\x2d\x31\x2e\x30\x35\
-\x32\x2e\x33\x35\x38\x36\x33\x20\x30\x20\x2e\x36\x36\x39\x34\x34\
-\x2e\x31\x34\x33\x34\x35\x2e\x38\x38\x34\x36\x31\x2e\x33\x38\x32\
-\x35\x34\x6c\x39\x2e\x39\x37\x30\x37\x20\x31\x30\x2e\x37\x38\x34\
-\x63\x2e\x32\x38\x36\x39\x30\x2e\x33\x33\x34\x37\x32\x2e\x35\x32\
-\x35\x39\x39\x2e\x36\x39\x33\x33\x35\x2e\x35\x32\x35\x39\x39\x20\
-\x31\x2e\x31\x32\x33\x37\x20\x30\x20\x2e\x34\x35\x34\x32\x36\x2d\
-\x2e\x33\x33\x34\x37\x32\x2e\x38\x31\x32\x38\x39\x2d\x2e\x39\x30\
-\x38\x35\x32\x2e\x38\x31\x32\x38\x39\x68\x2d\x33\x2e\x35\x38\x36\
-\x33\x6c\x32\x2e\x31\x32\x37\x39\x20\x34\x2e\x37\x31\x63\x2e\x30\
-\x39\x35\x36\x33\x2e\x32\x31\x35\x31\x38\x2e\x31\x34\x33\x34\x35\
-\x2e\x33\x38\x32\x35\x34\x2e\x31\x34\x33\x34\x35\x2e\x36\x32\x31\
-\x36\x32\x20\x30\x20\x2e\x33\x31\x30\x38\x31\x2d\x2e\x32\x36\x32\
-\x39\x39\x2e\x35\x39\x37\x37\x31\x2d\x2e\x35\x39\x37\x37\x31\x2e\
-\x37\x34\x31\x31\x36\x7a\x22\x2f\x3e\x3c\x2f\x73\x76\x67\x3e\
\x00\x00\x03\x80\
\x3c\
\x3f\x78\x6d\x6c\x20\x76\x65\x72\x73\x69\x6f\x6e\x3d\x22\x31\x2e\
@@ -5251,22 +5398,26 @@ qt_resource_name = b"\
\x0a\x6c\x78\x43\
\x00\x72\
\x00\x65\x00\x73\x00\x6f\x00\x75\x00\x72\x00\x63\x00\x65\x00\x73\
+\x00\x0a\
+\x0a\x68\x03\xe7\
+\x00\x63\
+\x00\x75\x00\x72\x00\x73\x00\x6f\x00\x72\x00\x2e\x00\x73\x00\x76\x00\x67\
\x00\x07\
-\x08\x73\x57\x87\
-\x00\x61\
-\x00\x70\x00\x70\x00\x2e\x00\x70\x00\x6e\x00\x67\
+\x0a\xc7\x5a\x07\
+\x00\x63\
+\x00\x75\x00\x74\x00\x2e\x00\x73\x00\x76\x00\x67\
\x00\x09\
\x02\xc5\xa9\x47\
\x00\x72\
\x00\x75\x00\x6c\x00\x65\x00\x72\x00\x2e\x00\x73\x00\x76\x00\x67\
+\x00\x10\
+\x02\xf8\x99\x27\
+\x00\x64\
+\x00\x65\x00\x66\x00\x61\x00\x75\x00\x6c\x00\x74\x00\x43\x00\x6f\x00\x6c\x00\x6f\x00\x72\x00\x2e\x00\x73\x00\x76\x00\x67\
\x00\x07\
-\x0a\xc7\x5a\x07\
-\x00\x63\
-\x00\x75\x00\x74\x00\x2e\x00\x73\x00\x76\x00\x67\
-\x00\x0a\
-\x0a\x68\x03\xe7\
-\x00\x63\
-\x00\x75\x00\x72\x00\x73\x00\x6f\x00\x72\x00\x2e\x00\x73\x00\x76\x00\x67\
+\x08\x73\x57\x87\
+\x00\x61\
+\x00\x70\x00\x70\x00\x2e\x00\x70\x00\x6e\x00\x67\
\x00\x09\
\x09\xc8\x8f\x47\
\x00\x63\
@@ -5279,13 +5430,14 @@ qt_resource_name = b"\
qt_resource_struct = b"\
\x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\
-\x00\x00\x00\x00\x00\x02\x00\x00\x00\x06\x00\x00\x00\x02\
-\x00\x00\x00\x2c\x00\x00\x00\x00\x00\x01\x00\x01\x37\x1f\
+\x00\x00\x00\x00\x00\x02\x00\x00\x00\x07\x00\x00\x00\x02\
+\x00\x00\x00\x46\x00\x00\x00\x00\x00\x01\x00\x00\x06\x5e\
+\x00\x00\x00\x5e\x00\x00\x00\x00\x00\x01\x00\x00\x08\xbd\
+\x00\x00\x00\x84\x00\x00\x00\x00\x00\x01\x00\x00\x11\xd0\
+\x00\x00\x00\x98\x00\x00\x00\x00\x00\x01\x00\x01\x48\xef\
\x00\x00\x00\x18\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\
-\x00\x00\x00\x72\x00\x00\x00\x00\x00\x01\x00\x01\x3f\xdc\
-\x00\x00\x00\x58\x00\x00\x00\x00\x00\x01\x00\x01\x3c\xa8\
-\x00\x00\x00\x44\x00\x00\x00\x00\x00\x01\x00\x01\x39\x7e\
-\x00\x00\x00\x8a\x00\x00\x00\x00\x00\x01\x00\x01\x43\x60\
+\x00\x00\x00\x32\x00\x00\x00\x00\x00\x01\x00\x00\x03\x34\
+\x00\x00\x00\xb0\x00\x00\x00\x00\x00\x01\x00\x01\x4c\x73\
"
def qInitResources():
diff --git a/Lib/defconQt/icons_db.qrc b/Lib/defconQt/icons_db.qrc
index 5fea427..9ec2d43 100644
--- a/Lib/defconQt/icons_db.qrc
+++ b/Lib/defconQt/icons_db.qrc
@@ -5,6 +5,7 @@
<file>resources/curve.svg</file>
<file>resources/cut.svg</file>
<file>resources/ruler.svg</file>
+ <file>resources/defaultColor.svg</file>
<file>resources/settings.svg</file>
</qresource>
</RCC>
diff --git a/Lib/defconQt/layerSetList.py b/Lib/defconQt/layerSetList.py
new file mode 100644
index 0000000..b2d3c9f
--- /dev/null
+++ b/Lib/defconQt/layerSetList.py
@@ -0,0 +1,135 @@
+from PyQt5.QtCore import Qt
+from PyQt5.QtGui import QKeySequence, QColor, QPixmap, QIcon
+from PyQt5.QtWidgets import QWidget, QMenu, QListWidget, QListWidgetItem, \
+ QAbstractItemView, QVBoxLayout, QAction, QColorDialog
+from defconQt import icons_db
+from defconQt.glyphView import AddLayerDialog
+
+class LayerSetList(QListWidget):
+ def __init__(self, font, parent=None, *args, **kwds):
+ super().__init__(parent, *args, **kwds)
+
+ self._layerSet = font.layers
+
+ self.setDragDropMode(QAbstractItemView.InternalMove)
+
+ model = self.model()
+ model.rowsMoved.connect(self._reordered)
+ self.itemChanged.connect(self._itemChanged)
+
+ self.setContextMenuPolicy(Qt.ActionsContextMenu)
+
+ action = QAction("Add Layer…", self)
+ action.setShortcuts(QKeySequence.New)
+ action.triggered.connect(self._addLayer)
+ self.addAction(action)
+
+ action = QAction("Change &Name", self)
+ action.setShortcuts(QKeySequence('n'))
+ action.triggered.connect(lambda : self.editItem(self.currentItem()))
+ self.addAction(action)
+
+ action = QAction("Change &Color", self)
+ action.setShortcuts(QKeySequence('c'))
+ action.triggered.connect(self._changeColor)
+ self.addAction(action)
+
+ action = QAction("Reset Color to &Default", self)
+ action.setShortcuts(QKeySequence('d'))
+ action.triggered.connect(self._resetColor)
+ self.addAction(action)
+
+ action = QAction("Delete", self)
+ action.setShortcuts(QKeySequence.Delete)
+ action.triggered.connect(self._deleteLayer)
+ self.addAction(action)
+
+ self._layerSet.addObserver(self, '_update', 'LayerSet.Changed')
+
+ self._update()
+
+ def _update(self, *args):
+ index = self.currentRow()
+ while self.count():
+ self.takeItem(self.count()-1)
+ for i, layer in enumerate(self._layerSet):
+ item = self._makeItem(layer)
+ self.addItem(item)
+ if i == index:
+ self.setCurrentItem(item)
+
+ def _makeItem(self, layer):
+ isDefault = layer is self._layerSet.defaultLayer
+ name = layer.name
+ color = layer.color
+ item = QListWidgetItem()
+ item.setText(name)
+ if color:
+ pixmap = QPixmap(100, 100)
+ # change color
+ pixmap.fill(QColor.fromRgbF(*color))
+ icon = QIcon(pixmap)
+ else:
+ icon = QIcon(":/resources/defaultColor.svg")
+ item.setIcon(icon)
+
+ if isDefault:
+ font = item.font()
+ font.setBold(True)
+ item.setFont(font)
+
+ item.setFlags(item.flags() | Qt.ItemIsEditable)
+
+ return item;
+
+ def _getCurrentLayer(self):
+ item = self.currentItem()
+ if not item:
+ return
+ name = item.text()
+ return self._layerSet[name] if name in self._layerSet else None
+
+ def _deleteLayer(self):
+ layer = self._getCurrentLayer()
+ if not layer:
+ return
+
+ if layer is self._layerSet.defaultLayer:
+ # because I think we can't handle a font without a default layer
+ # TODO: try this
+ return
+ del self._layerSet[layer.name];
+
+ def _reordered(self, *args):
+ # get a new layer order
+ newOrder = [self.item(index).text() for index in range(self.count())]
+ self._layerSet.layerOrder = newOrder
+
+ def _itemChanged(self, item):
+ index = self.indexFromItem(item).row()
+ layerName = self._layerSet.layerOrder[index]
+ self._layerSet[layerName].name = item.text()
+
+ def _addLayer(self):
+ newLayerName, ok = AddLayerDialog.getNewLayerName(self)
+ if ok:
+ # this should cause self._layerSetLayerAdded to be executed
+ self._layerSet.newLayer(newLayerName)
+
+ def _changeColor(self):
+ layer = self._getCurrentLayer()
+ if not layer:
+ return
+
+ startColor = layer.color and QColor.fromRgbF(*layer.color) or QColor('limegreen')
+ qcolor = QColorDialog.getColor(startColor, self, options=QColorDialog.ShowAlphaChannel)
+ if not qcolor.isValid():
+ # cancelled
+ return
+ layer.color = qcolor.getRgbF()
+
+ def _resetColor(self):
+ layer = self._getCurrentLayer()
+ if not layer:
+ return
+ layer.color = None
diff --git a/Lib/defconQt/representationFactories/qPainterPathFactory.py b/Lib/defconQt/representationFactories/qPainterPathFactory.py
index cb51de6..0b99b82 100644
--- a/Lib/defconQt/representationFactories/qPainterPathFactory.py
+++ b/Lib/defconQt/representationFactories/qPainterPathFactory.py
@@ -2,7 +2,7 @@ from fontTools.pens.qtPen import QtPen
from PyQt5.QtCore import Qt
def QPainterPathFactory(glyph):
- pen = QtPen(glyph.getParent())
+ pen = QtPen(glyph.layer)
glyph.draw(pen)
pen.path.setFillRule(Qt.WindingFill)
return pen.path
diff --git a/Lib/defconQt/resources/defaultColor.svg b/Lib/defconQt/resources/defaultColor.svg
new file mode 100644
index 0000000..0c343cb
--- /dev/null
+++ b/Lib/defconQt/resources/defaultColor.svg
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+ xmlns:dc="http://purl.org/dc/elements/1.1/"
+ xmlns:cc="http://creativecommons.org/ns#"
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ xmlns="http://www.w3.org/2000/svg"
+ xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+ xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+ version="1.1"
+ width="24"
+ height="24"
+ viewBox="0 0 24 24"
+ id="svg2"
+ inkscape:version="0.91 r13725"
+ sodipodi:docname="defaultColor.svg">
+ <metadata
+ id="metadata10">
+ <rdf:RDF>
+ <cc:Work
+ rdf:about="">
+ <dc:format>image/svg+xml</dc:format>
+ <dc:type
+ rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+ <dc:title />
+ </cc:Work>
+ </rdf:RDF>
+ </metadata>
+ <defs
+ id="defs8" />
+ <sodipodi:namedview
+ pagecolor="#ffffff"
+ bordercolor="#666666"
+ borderopacity="1"
+ objecttolerance="10"
+ gridtolerance="10"
+ guidetolerance="10"
+ inkscape:pageopacity="0"
+ inkscape:pageshadow="2"
+ inkscape:window-width="1920"
+ inkscape:window-height="1171"
+ id="namedview6"
+ showgrid="false"
+ inkscape:zoom="13.906433"
+ inkscape:cx="11.775211"
+ inkscape:cy="7.2423214"
+ inkscape:window-x="0"
+ inkscape:window-y="2"
+ inkscape:window-maximized="1"
+ inkscape:current-layer="svg2" />
+ <rect
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
+ id="rect4141"
+ width="24"
+ height="24"
+ x="0"
+ y="0" />
+ <path
+ style="color:#000000;display:inline;overflow:visible;visibility:visible;opacity:1;fill:#ff0000;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;enable-background:accumulate"
+ d="M 17.546875 0 L 0 17.546875 L 0 24 L 5.5664062 24 L 24 5.5664062 L 24 0 L 17.546875 0 z "
+ id="rect4145" />
+</svg>