diff options
Diffstat (limited to 'Lib')
| -rw-r--r-- | Lib/defconQt/LayerSetList.py | 135 | ||||
| -rw-r--r-- | Lib/defconQt/fontView.py | 8 | ||||
| -rw-r--r-- | Lib/defconQt/glyphView.py | 722 | ||||
| -rw-r--r-- | Lib/defconQt/icons_db.py | 478 | ||||
| -rw-r--r-- | Lib/defconQt/icons_db.qrc | 1 | ||||
| -rw-r--r-- | Lib/defconQt/layerSetList.py | 135 | ||||
| -rw-r--r-- | Lib/defconQt/representationFactories/qPainterPathFactory.py | 2 | ||||
| -rw-r--r-- | Lib/defconQt/resources/defaultColor.svg | 62 |
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> |
