From deeac2c99b5b49d2ec198c03c238c6e12a505277 Mon Sep 17 00:00:00 2001 From: sendiulo Date: Tue, 27 Aug 2013 11:47:14 +0200 Subject: [PATCH 01/22] Identicons working in most lists, but not working for new items yet. There are no settings yet. --- src/bitmessageqt/__init__.py | 79 ++++++- src/bitmessageqt/settings.py | 2 +- src/identicon.py | 262 ++++++++++++++++++++++ src/identicon_py_license | 11 + src/pydenticon.py | 409 +++++++++++++++++++++++++++++++++++ 5 files changed, 759 insertions(+), 4 deletions(-) create mode 100644 src/identicon.py create mode 100644 src/identicon_py_license create mode 100644 src/pydenticon.py diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 3e513295..84136424 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -47,6 +47,20 @@ except Exception as err: print 'Error message:', err sys.exit() +# for the md5 hash (used for the identicons) +import hashlib + +# load identicon code +# :Author:Shin Adachi +# Licesensed under FreeBSD License. +import identicon +# usage: identicon.render_identicon(code, size) +# requires PIL + +# load another identicon code +# http://boottunes.googlecode.com/svn-history/r302/trunk/src/pydenticon.py +from pydenticon import Pydenticon + try: _encoding = QtGui.QApplication.UnicodeUTF8 except AttributeError: @@ -55,6 +69,34 @@ except AttributeError: def _translate(context, text): return QtGui.QApplication.translate(context, text) +def identiconize(address): + suffix = "" # here you could put "@bitmessge.ch" or "@bm.addr" to make it compatible with other identicon generators + # instead, you could also use a pseudo-password to salt the generation of the identicons + # Attacks where someone creates an address to mimic someone else's identicon should be impossible then + # i think it should generate a random string by default + + # hash = hashlib.md5(addBMIfNotPresent(address)+suffix).hexdigest()[:8] + # print hash + ##japanese code + # idcon_render = identicon.render_identicon(int(hash, 16), 8) + # idcon_render.save('images/'+hash+'.png') + # im = idcon_render + # http://qt-project.org/forums/viewthread/5866 + # from PIL import Image + # from PyQt4.QtGui import QImage, QImageReader, QLabel, QPixmap, QApplication + + # PHP-like code + idcon_render = Pydenticon(addBMIfNotPresent(address)+suffix) + image = idcon_render._render() + + # im = Image.open('images/'+hash+'.png') + # http://stackoverflow.com/questions/6756820/python-pil-image-tostring + data = image.convert("RGBA").tostring("raw", "RGBA") + image = QImage(data, image.size[0], image.size[1], QImage.Format_ARGB32) + pix = QPixmap.fromImage(image) + idcon = QtGui.QIcon() + idcon.addPixmap(pix, QtGui.QIcon.Normal, QtGui.QIcon.Off) + return idcon class MyForm(QtGui.QMainWindow): @@ -327,6 +369,7 @@ class MyForm(QtGui.QMainWindow): newItem.setTextColor(QtGui.QColor(128, 128, 128)) if shared.safeConfigGetBoolean(addressInKeysFile, 'mailinglist'): newItem.setTextColor(QtGui.QColor(137, 04, 177)) # magenta + newItem.setIcon(identiconize(addressInKeysFile)) self.ui.tableWidgetYourIdentities.setItem(0, 1, newItem) newItem = QtGui.QTableWidgetItem(str( addressStream(addressInKeysFile))) @@ -354,9 +397,11 @@ class MyForm(QtGui.QMainWindow): for row in queryreturn: label, address = row self.ui.tableWidgetAddressBook.insertRow(0) + # address book item newItem = QtGui.QTableWidgetItem(unicode(label, 'utf-8')) self.ui.tableWidgetAddressBook.setItem(0, 0, newItem) newItem = QtGui.QTableWidgetItem(address) + newItem.setIcon(identiconize(address)) newItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) self.ui.tableWidgetAddressBook.setItem(0, 1, newItem) @@ -401,6 +446,16 @@ class MyForm(QtGui.QMainWindow): self.numberOfBroadcastsProcessed = 0 self.numberOfPubkeysProcessed = 0 + # Set the icon sizes for the identicons + identicon_size = 3*7 + self.ui.tableWidgetInbox.setIconSize(QtCore.QSize(identicon_size, identicon_size)) + self.ui.tableWidgetSent.setIconSize(QtCore.QSize(identicon_size, identicon_size)) + self.ui.tableWidgetYourIdentities.setIconSize(QtCore.QSize(identicon_size, identicon_size)) + self.ui.tableWidgetSubscriptions.setIconSize(QtCore.QSize(identicon_size, identicon_size)) + self.ui.tableWidgetAddressBook.setIconSize(QtCore.QSize(identicon_size, identicon_size)) + self.ui.tableWidgetWhitelist.setIconSize(QtCore.QSize(identicon_size, identicon_size)) + self.ui.tableWidgetBlacklist.setIconSize(QtCore.QSize(identicon_size, identicon_size)) + self.UISignalThread = UISignaler() QtCore.QObject.connect(self.UISignalThread, QtCore.SIGNAL( "writeNewAddressToTable(PyQt_PyObject,PyQt_PyObject,PyQt_PyObject)"), self.writeNewAddressToTable) @@ -607,6 +662,7 @@ class MyForm(QtGui.QMainWindow): else: newItem = QtGui.QTableWidgetItem(unicode(toLabel, 'utf-8')) newItem.setToolTip(unicode(toLabel, 'utf-8')) + newItem.setIcon(identiconize(toAddress)) newItem.setData(Qt.UserRole, str(toAddress)) newItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) @@ -618,6 +674,7 @@ class MyForm(QtGui.QMainWindow): else: newItem = QtGui.QTableWidgetItem(unicode(fromLabel, 'utf-8')) newItem.setToolTip(unicode(fromLabel, 'utf-8')) + newItem.setIcon(identiconize(fromAddress)) newItem.setData(Qt.UserRole, str(fromAddress)) newItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) @@ -747,10 +804,12 @@ class MyForm(QtGui.QMainWindow): if queryreturn != []: for row in queryreturn: fromLabel, = row - + + # message row self.ui.tableWidgetInbox.insertRow(0) newItem = QtGui.QTableWidgetItem(unicode(toLabel, 'utf-8')) newItem.setToolTip(unicode(toLabel, 'utf-8')) + # to newItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) if not read: @@ -760,7 +819,9 @@ class MyForm(QtGui.QMainWindow): newItem.setTextColor(QtGui.QColor(137, 04, 177)) if shared.safeConfigGetBoolean(str(toAddress), 'chan'): newItem.setTextColor(QtGui.QColor(216, 119, 0)) # orange + newItem.setIcon(identiconize(toAddress)) self.ui.tableWidgetInbox.setItem(0, 0, newItem) + # from if fromLabel == '': newItem = QtGui.QTableWidgetItem( unicode(fromAddress, 'utf-8')) @@ -773,8 +834,9 @@ class MyForm(QtGui.QMainWindow): if not read: newItem.setFont(font) newItem.setData(Qt.UserRole, str(fromAddress)) - + newItem.setIcon(identiconize(fromAddress)) self.ui.tableWidgetInbox.setItem(0, 1, newItem) + # subject newItem = QtGui.QTableWidgetItem(unicode(subject, 'utf-8')) newItem.setToolTip(unicode(subject, 'utf-8')) newItem.setData(Qt.UserRole, unicode(message, 'utf-8)')) @@ -783,6 +845,7 @@ class MyForm(QtGui.QMainWindow): if not read: newItem.setFont(font) self.ui.tableWidgetInbox.setItem(0, 2, newItem) + # time received newItem = myTableWidgetItem(unicode(strftime(shared.config.get( 'bitmessagesettings', 'timeformat'), localtime(int(received))), 'utf-8')) newItem.setToolTip(unicode(strftime(shared.config.get( @@ -1473,6 +1536,8 @@ class MyForm(QtGui.QMainWindow): toLabel = toAddress self.ui.tableWidgetInbox.item( i, 0).setText(unicode(toLabel, 'utf-8')) + self.ui.tableWidgetInbox.item( + i, 0).setIcon(identiconize(toAddress)) # Set the color according to whether it is the address of a mailing # list or not. if shared.safeConfigGetBoolean(toAddress, 'mailinglist'): @@ -1493,6 +1558,8 @@ class MyForm(QtGui.QMainWindow): fromLabel = fromAddress self.ui.tableWidgetSent.item( i, 1).setText(unicode(fromLabel, 'utf-8')) + self.ui.tableWidgetSent.item( + i, 0).setIcon(identiconize(fromAddress)) def rerenderSentToLabels(self): for i in range(self.ui.tableWidgetSent.rowCount()): @@ -1512,7 +1579,7 @@ class MyForm(QtGui.QMainWindow): toLabel, = row self.ui.tableWidgetSent.item( i, 0).setText(unicode(toLabel, 'utf-8')) - + def rerenderSubscriptions(self): self.ui.tableWidgetSubscriptions.setRowCount(0) shared.sqlLock.acquire() @@ -1529,6 +1596,7 @@ class MyForm(QtGui.QMainWindow): newItem.setTextColor(QtGui.QColor(128, 128, 128)) self.ui.tableWidgetSubscriptions.setItem(0, 0, newItem) newItem = QtGui.QTableWidgetItem(address) + newItem.setIcon(identiconize(address)) newItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) if not enabled: @@ -1791,6 +1859,7 @@ class MyForm(QtGui.QMainWindow): newItem = QtGui.QTableWidgetItem(unicode(fromLabel, 'utf-8')) newItem.setToolTip(unicode(fromLabel, 'utf-8')) newItem.setData(Qt.UserRole, str(fromAddress)) + newItem.setIcon(identiconize(address)) self.ui.tableWidgetSent.setItem(0, 1, newItem) newItem = QtGui.QTableWidgetItem(unicode(subject, 'utf-8)')) newItem.setToolTip(unicode(subject, 'utf-8)')) @@ -1920,6 +1989,7 @@ class MyForm(QtGui.QMainWindow): newItem = QtGui.QTableWidgetItem(unicode(label, 'utf-8')) self.ui.tableWidgetAddressBook.setItem(0, 0, newItem) newItem = QtGui.QTableWidgetItem(address) + newItem.setIcon(identiconize(address)) newItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) self.ui.tableWidgetAddressBook.setItem(0, 1, newItem) @@ -1949,6 +2019,7 @@ class MyForm(QtGui.QMainWindow): newItem = QtGui.QTableWidgetItem(unicode(label, 'utf-8')) self.ui.tableWidgetSubscriptions.setItem(0,0,newItem) newItem = QtGui.QTableWidgetItem(address) + newItem.setIcon(identiconize(address)) newItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled ) self.ui.tableWidgetSubscriptions.setItem(0,1,newItem) self.ui.tableWidgetSubscriptions.setSortingEnabled(True) @@ -1998,6 +2069,7 @@ class MyForm(QtGui.QMainWindow): newItem.setTextColor(QtGui.QColor(128, 128, 128)) self.ui.tableWidgetBlacklist.setItem(0, 0, newItem) newItem = QtGui.QTableWidgetItem(address) + newItem.setIcon(identiconize(address)) newItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) if not enabled: @@ -2971,6 +3043,7 @@ class MyForm(QtGui.QMainWindow): self.ui.tableWidgetYourIdentities.setItem( 0, 0, newItem) newItem = QtGui.QTableWidgetItem(address) + newItem.setIcon(identiconize(address)) newItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) if shared.safeConfigGetBoolean(address, 'chan'): diff --git a/src/bitmessageqt/settings.py b/src/bitmessageqt/settings.py index ad597773..821df6cf 100644 --- a/src/bitmessageqt/settings.py +++ b/src/bitmessageqt/settings.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'settings.ui' # -# Created: Sat Aug 24 09:19:58 2013 +# Created: Sun Aug 25 18:09:38 2013 # by: PyQt4 UI code generator 4.10.2 # # WARNING! All changes made in this file will be lost! diff --git a/src/identicon.py b/src/identicon.py new file mode 100644 index 00000000..fae5a250 --- /dev/null +++ b/src/identicon.py @@ -0,0 +1,262 @@ +#!/usr/bin/env python +# -*- coding:utf-8 -*- +""" +identicon.py +identicon python implementation. +by Shin Adachi + += usage = + +== commandline == +>>> python identicon.py [code] + +== python == +>>> import identicon +>>> identicon.render_identicon(code, size) + +Return a PIL Image class instance which have generated identicon image. +```size``` specifies `patch size`. Generated image size is 3 * ```size```. +""" +# g +# PIL Modules +import Image +import ImageDraw +import ImagePath +import ImageColor + + +__all__ = ['render_identicon', 'IdenticonRendererBase'] + + +class Matrix2D(list): + """Matrix for Patch rotation""" + def __init__(self, initial=[0.] * 9): + assert isinstance(initial, list) and len(initial) == 9 + list.__init__(self, initial) + + def clear(self): + for i in xrange(9): + self[i] = 0. + + def set_identity(self): + self.clear() + for i in xrange(3): + self[i] = 1. + + def __str__(self): + return '[%s]' % ', '.join('%3.2f' % v for v in self) + + def __mul__(self, other): + r = [] + if isinstance(other, Matrix2D): + for y in xrange(3): + for x in xrange(3): + v = 0.0 + for i in xrange(3): + v += (self[i * 3 + x] * other[y * 3 + i]) + r.append(v) + else: + raise NotImplementedError + return Matrix2D(r) + + def for_PIL(self): + return self[0:6] + + @classmethod + def translate(kls, x, y): + return kls([1.0, 0.0, float(x), + 0.0, 1.0, float(y), + 0.0, 0.0, 1.0]) + + @classmethod + def scale(kls, x, y): + return kls([float(x), 0.0, 0.0, + 0.0, float(y), 0.0, + 0.0, 0.0, 1.0]) + + """ + # need `import math` + @classmethod + def rotate(kls, theta, pivot=None): + c = math.cos(theta) + s = math.sin(theta) + + matR = kls([c, -s, 0., s, c, 0., 0., 0., 1.]) + if not pivot: + return matR + return kls.translate(-pivot[0], -pivot[1]) * matR * + kls.translate(*pivot) + """ + + @classmethod + def rotateSquare(kls, theta, pivot=None): + theta = theta % 4 + c = [1., 0., -1., 0.][theta] + s = [0., 1., 0., -1.][theta] + + matR = kls([c, -s, 0., s, c, 0., 0., 0., 1.]) + if not pivot: + return matR + return kls.translate(-pivot[0], -pivot[1]) * matR * \ + kls.translate(*pivot) + + +class IdenticonRendererBase(object): + PATH_SET = [] + + def __init__(self, code): + """ + @param code code for icon + """ + if not isinstance(code, int): + code = int(code) + self.code = code + + def render(self, size): + """ + render identicon to PIL.Image + + @param size identicon patchsize. (image size is 3 * [size]) + @return PIL.Image + """ + + # decode the code + middle, corner, side, foreColor, backColor = self.decode(self.code) + + # make image + image = Image.new("RGB", (size * 3, size * 3)) + draw = ImageDraw.Draw(image) + + # fill background + draw.rectangle((0, 0, image.size[0], image.size[1]), fill=0) + + kwds = { + 'draw': draw, + 'size': size, + 'foreColor': foreColor, + 'backColor': backColor} + # middle patch + self.drawPatch((1, 1), middle[2], middle[1], middle[0], **kwds) + + # side patch + kwds['type'] = side[0] + for i in xrange(4): + pos = [(1, 0), (2, 1), (1, 2), (0, 1)][i] + self.drawPatch(pos, side[2] + 1 + i, side[1], **kwds) + + # corner patch + kwds['type'] = corner[0] + for i in xrange(4): + pos = [(0, 0), (2, 0), (2, 2), (0, 2)][i] + self.drawPatch(pos, corner[2] + 1 + i, corner[1], **kwds) + + return image + + def drawPatch(self, pos, turn, invert, type, draw, size, foreColor, + backColor): + """ + @param size patch size + """ + path = self.PATH_SET[type] + if not path: + # blank patch + invert = not invert + path = [(0., 0.), (1., 0.), (1., 1.), (0., 1.), (0., 0.)] + patch = ImagePath.Path(path) + if invert: + foreColor, backColor = backColor, foreColor + + mat = Matrix2D.rotateSquare(turn, pivot=(0.5, 0.5)) *\ + Matrix2D.translate(*pos) *\ + Matrix2D.scale(size, size) + + patch.transform(mat.for_PIL()) + draw.rectangle((pos[0] * size, pos[1] * size, (pos[0] + 1) * size, + (pos[1] + 1) * size), fill=backColor) + draw.polygon(patch, fill=foreColor, outline=foreColor) + + ### virtual functions + def decode(self, code): + raise NotImplementedError + + +class DonRenderer(IdenticonRendererBase): + """ + Don Park's implementation of identicon + see : http://www.docuverse.com/blog/donpark/2007/01/19/identicon-updated-and-source-released + """ + + PATH_SET = [ + [(0, 0), (4, 0), (4, 4), (0, 4)], # 0 + [(0, 0), (4, 0), (0, 4)], + [(2, 0), (4, 4), (0, 4)], + [(0, 0), (2, 0), (2, 4), (0, 4)], + [(2, 0), (4, 2), (2, 4), (0, 2)], # 4 + [(0, 0), (4, 2), (4, 4), (2, 4)], + [(2, 0), (4, 4), (2, 4), (3, 2), (1, 2), (2, 4), (0, 4)], + [(0, 0), (4, 2), (2, 4)], + [(1, 1), (3, 1), (3, 3), (1, 3)], # 8 + [(2, 0), (4, 0), (0, 4), (0, 2), (2, 2)], + [(0, 0), (2, 0), (2, 2), (0, 2)], + [(0, 2), (4, 2), (2, 4)], + [(2, 2), (4, 4), (0, 4)], + [(2, 0), (2, 2), (0, 2)], + [(0, 0), (2, 0), (0, 2)], + []] # 15 + MIDDLE_PATCH_SET = [0, 4, 8, 15] + + # modify path set + for idx in xrange(len(PATH_SET)): + if PATH_SET[idx]: + p = map(lambda vec: (vec[0] / 4.0, vec[1] / 4.0), PATH_SET[idx]) + PATH_SET[idx] = p + p[:1] + + def decode(self, code): + # decode the code + middleType = self.MIDDLE_PATCH_SET[code & 0x03] + middleInvert= (code >> 2) & 0x01 + cornerType = (code >> 3) & 0x0F + cornerInvert= (code >> 7) & 0x01 + cornerTurn = (code >> 8) & 0x03 + sideType = (code >> 10) & 0x0F + sideInvert = (code >> 14) & 0x01 + sideTurn = (code >> 15) & 0x03 + # bug reported by Masato Hagiwara + # http://lilyx.net/iconlang-en/ + # blue = (code >> 16) & 0x1F + # green = (code >> 21) & 0x1F + blue = (code >> 17) & 0x1F + green = (code >> 22) & 0x1F + red = (code >> 27) & 0x1F + + foreColor = (red << 3, green << 3, blue << 3) + + return (middleType, middleInvert, 0),\ + (cornerType, cornerInvert, cornerTurn),\ + (sideType, sideInvert, sideTurn),\ + foreColor, ImageColor.getrgb('white') + + +def render_identicon(code, size, renderer=None): + if not renderer: + renderer = DonRenderer + return renderer(code).render(size) + + +if __name__ == '__main__': + import sys + + if len(sys.argv) < 2: + print 'usage: python identicon.py [CODE]....' + raise SystemExit + + for code in sys.argv[1:]: + if code.startswith('0x') or code.startswith('0X'): + code = int(code[2:], 16) + elif code.startswith('0'): + code = int(code[1:], 8) + else: + code = int(code) + + icon = render_identicon(code, 24) + icon.save('%08x.png' % code, 'PNG') diff --git a/src/identicon_py_license b/src/identicon_py_license new file mode 100644 index 00000000..e6e964fb --- /dev/null +++ b/src/identicon_py_license @@ -0,0 +1,11 @@ +identicon.py is Licesensed under FreeBSD License. +(http://www.freebsd.org/copyright/freebsd-license.html) + +Copyright 1994-2009 Shin Adachi. All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/pydenticon.py b/src/pydenticon.py new file mode 100644 index 00000000..57076093 --- /dev/null +++ b/src/pydenticon.py @@ -0,0 +1,409 @@ +""" +Python implementation of PHP Identicons, found here: +http://sourceforge.net/projects/identicons/ + +Licensed under the GPLv3. +""" +from PIL import Image, ImageDraw, ImagePath, ImageColor +from math import fabs as abs +from hashlib import md5 +import StringIO + +class Pydenticon: + """ + Generate an identicon. + + """ + + WHITE = (255, 255, 255) + + def __init__(self, input, size = 128): + self.hash = md5(input).hexdigest() + self.size = size + + def as_string(self): + """ + Return the image as a string representation of a PNG file. + + """ + if not hasattr(self, 'image'): + self.image = self._render() + output = StringIO.StringIO() + self.image.save(output, format="PNG") + contents = output.getvalue() + output.close() + return contents + + def save(self, filename): + """ + Save the result as a PNG file. + + """ + if not hasattr(self, 'image'): + self.image = self._render() + self.image.save(filename, format="PNG") + + def _get_sprite(self, shape, r, g, b, rotation): + """ + Generate sprite for corners and sides and returnro a PIL.Image object. + + """ + sprite = Image.new("RGB", (self.size, self.size), self.WHITE) + + if shape == 0: # triangle + points = [ + 0.5,1, + 1,0, + 1,1 + ] + elif shape == 1: #parallelogram + points = [ + 0.5,0, + 1,0, + 0.5,1, + 0,1 + ] + elif shape == 2: # mouse ears + points = [ + 0.5,0, + 1,0, + 1,1, + 0.5,1, + 1,0.5 + ] + elif shape == 3: # ribbon + points = [ + 0,0.5, + 0.5,0, + 1,0.5, + 0.5,1, + 0.5,0.5 + ] + elif shape == 4: # sails + points = [ + 0,0.5, + 1,0, + 1,1, + 0,1, + 1,0.5 + ] + elif shape == 5: # fins + points = [ + 1,0, + 1,1, + 0.5,1, + 1,0.5, + 0.5,0.5 + ] + elif shape == 6: # beak + points = [ + 0,0, + 1,0, + 1,0.5, + 0,0, + 0.5,1, + 0,1 + ] + elif shape == 7: # chevron + points = [ + 0,0, + 0.5,0, + 1,0.5, + 0.5,1, + 0,1, + 0.5,0.5 + ] + elif shape == 8: # fish + points = [ + 0.5,0, + 0.5,0.5, + 1,0.5, + 1,1, + 0.5,1, + 0.5,0.5, + 0,0.5 + ] + elif shape == 9: # kite + points = [ + 0,0, + 1,0, + 0.5,0.5, + 1,0.5, + 0.5,1, + 0.5,0.5, + 0,1 + ] + elif shape == 10: # trough + points = [ + 0,0.5, + 0.5,1, + 1,0.5, + 0.5,0, + 1,0, + 1,1, + 0,1 + ] + elif shape == 11: # rays + points = [ + 0.5,0, + 1,0, + 1,1, + 0.5,1, + 1,0.75, + 0.5,0.5, + 1,0.25 + ] + elif shape == 12: # double rhombus + points = [ + 0,0.5, + 0.5,0, + 0.5,0.5, + 1,0, + 1,0.5, + 0.5,1, + 0.5,0.5, + 0,1 + ] + elif shape == 13: # crown + points = [ + 0,0, + 1,0, + 1,1, + 0,1, + 1,0.5, + 0.5,0.25, + 0.5,0.75, + 0,0.5, + 0.5,0.25 + ] + elif shape == 14: # radioactive + points = [ + 0,0.5, + 0.5,0.5, + 0.5,0, + 1,0, + 0.5,0.5, + 1,0.5, + 0.5,1, + 0.5,0.5, + 0,1 + ] + else: # tiles + points = [ + 0,0, + 1,0, + 0.5,0.5, + 0.5,0, + 0,0.5, + 1,0.5, + 0.5,1, + 0.5,0.5, + 0,1 + ] + + # apply ratios + for i in range(0, len(points)): + points[i] = points[i] * self.size + + draw = ImageDraw.Draw(sprite) + draw.polygon(points, fill=(r, g, b)) + + for i in range(rotation): + sprite = sprite.rotate(90) + + return sprite + + def _get_center(self, shape, fR, fG, fB, bR, bG, bB, useBg): + """ + Generate sprite for center block and return a PIL.Image object. + + """ + sprite = Image.new("RGB", (self.size, self.size), self.WHITE) + + # make sure there's enough contrast before we use background color of side sprite + sufficient_contrast = ( + abs(fR - bR) > 127 or abs(fG - bG) > 127 or abs(fB - bB) > 127 + ) + if useBg > 0 and sufficient_contrast: + bg = (bR, bG, bB) + else: + bg = (255, 255, 255) + + if shape == 0: # empty + points = [] + + elif shape == 1: # fill + points = [ + 0,0, + 1,0, + 1,1, + 0,1 + ] + elif shape == 2: # diamond + points = [ + 0.5,0, + 1,0.5, + 0.5,1, + 0,0.5 + ] + elif shape == 3: # reverse diamond + points = [ + 0,0, + 1,0, + 1,1, + 0,1, + 0,0.5, + 0.5,1, + 1,0.5, + 0.5,0, + 0,0.5 + ] + elif shape == 4: # cross + points = [ + 0.25,0, + 0.75,0, + 0.5,0.5, + 1,0.25, + 1,0.75, + 0.5,0.5, + 0.75,1, + 0.25,1, + 0.5,0.5, + 0,0.75, + 0,0.25, + 0.5,0.5 + ] + elif shape == 5: # morning star + points = [ + 0,0, + 0.5,0.25, + 1,0, + 0.75,0.5, + 1,1, + 0.5,0.75, + 0,1, + 0.25,0.5 + ] + elif shape == 6: # small square + points = [ + 0.33,0.33, + 0.67,0.33, + 0.67,0.67, + 0.33,0.67 + ] + elif shape == 7: # checkerboard + points = [ + 0,0, + 0.33,0, + 0.33,0.33, + 0.66,0.33, + 0.67,0, + 1,0, + 1,0.33, + 0.67,0.33, + 0.67,0.67, + 1,0.67, + 1,1, + 0.67,1, + 0.67,0.67, + 0.33,0.67, + 0.33,1, + 0,1, + 0,0.67, + 0.33,0.67, + 0.33,0.33, + 0,0.33 + ] + + # apply ratios + for i in range(0, len(points)): + points[i] = points[i] * self.size + + if len(points) > 0: + draw = ImageDraw.Draw(sprite) + draw.polygon(points, fill=(fR, fG, fB)) + + return sprite + + def _render(self): + """ + Render the image and return the PIL.Image object. + + """ + # parse hash string + corner_sprite_shape = int(self.hash[0:1], 16) + side_sprite_shape = int(self.hash[1:2], 16) + center_sprite_shape = int(self.hash[2:3], 16) & 7 + + corner_sprite_rot = int(self.hash[3:4], 16) & 3 + side_sprite_rot = int(self.hash[4:5], 16) & 3 + center_sprite_bg = int(self.hash[5:6], 16) % 2 + + # corner sprite foreground color + corner_sprite_fg_r = int(self.hash[6:8], 16) + corner_sprite_fg_g = int(self.hash[8:10], 16) + corner_sprite_fg_b = int(self.hash[10:12], 16) + + # side sprite foreground color + side_sprite_fg_r = int(self.hash[12:14], 16) + side_sprite_fg_g = int(self.hash[14:16], 16) + side_sprite_fg_b = int(self.hash[16:18], 16) + + # final angle of rotation + angle = int(self.hash[18:20], 16) + + # start with blank 3X sized identicon + identicon = Image.new("RGB", (self.size*3, self.size*3), self.WHITE) + + # generate corner sprites + corner = self._get_sprite( + corner_sprite_shape, + corner_sprite_fg_r, + corner_sprite_fg_g, + corner_sprite_fg_b, + corner_sprite_rot + ) + identicon.paste(corner, (0, 0)) + corner = corner.rotate(90) + identicon.paste(corner, (0, self.size*2)) + corner = corner.rotate(90) + identicon.paste(corner, (self.size*2, self.size*2)) + corner = corner.rotate(90) + identicon.paste(corner, (self.size*2, 0)) + + # generate side sprites + side = self._get_sprite( + side_sprite_shape, + side_sprite_fg_r, + side_sprite_fg_g, + side_sprite_fg_b, + side_sprite_rot + ) + identicon.paste(side, (self.size, 0)) + side = side.rotate(90) + identicon.paste(side, (0, self.size)) + side = side.rotate(90) + identicon.paste(side, (self.size, self.size*2)) + side = side.rotate(90) + identicon.paste(side, (self.size*2, self.size)) + + # generate center sprite + center = self._get_center( + center_sprite_shape, + corner_sprite_fg_r, + corner_sprite_fg_g, + corner_sprite_fg_b, + side_sprite_fg_r, + side_sprite_fg_g, + side_sprite_fg_b, + center_sprite_bg + ) + identicon.paste(center, (self.size, self.size)) + + # resize image + resized = identicon.resize( + (self.size, self.size), + Image.ANTIALIAS + ) + + return resized \ No newline at end of file From 07b67051c97611a3a4e06732ab4fe850c72930ed Mon Sep 17 00:00:00 2001 From: sendiulo Date: Wed, 28 Aug 2013 16:50:46 +0200 Subject: [PATCH 02/22] Identicons now in new messages and in the From combobox. [Broadcast Subscribers] gets the Bitmessage Icon for identification. This is hard-coded now, I would favour to put [Broadcast Subscribers] as a fixed item into the Address Book, so you can set an avatar as soon as i implement avatars. Still no options yet: There will be an option to disable Identicons and Avatars separately. Another option will be for the suffix (to mimic adresses like "BM-...@bitmessage.ch" or to add a personal "salt" to prevent identicon attacks where someone creates an address with an identicon that looks alike the one of a known address). Also, identicon size (coupled with row height) should be available, but is of low priority. Next step (after the settings) is to load specific avatars for each address. --- src/bitmessageqt/__init__.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 84136424..784bd6f9 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -70,6 +70,11 @@ def _translate(context, text): return QtGui.QApplication.translate(context, text) def identiconize(address): + youdontwantidenticons = False + if youdontwantidenticons == True: + idcon = QtGui.QIcon() + return idcon + suffix = "" # here you could put "@bitmessge.ch" or "@bm.addr" to make it compatible with other identicon generators # instead, you could also use a pseudo-password to salt the generation of the identicons # Attacks where someone creates an address to mimic someone else's identicon should be impossible then @@ -85,6 +90,11 @@ def identiconize(address): # from PIL import Image # from PyQt4.QtGui import QImage, QImageReader, QLabel, QPixmap, QApplication + str_broadcast_subscribers = '[Broadcast subscribers]' + if address == str_broadcast_subscribers: + idcon = QtGui.QIcon(":/newPrefix/images/can-icon-24px.png") + return idcon + # PHP-like code idcon_render = Pydenticon(addBMIfNotPresent(address)+suffix) image = idcon_render._render() @@ -453,7 +463,7 @@ class MyForm(QtGui.QMainWindow): self.ui.tableWidgetYourIdentities.setIconSize(QtCore.QSize(identicon_size, identicon_size)) self.ui.tableWidgetSubscriptions.setIconSize(QtCore.QSize(identicon_size, identicon_size)) self.ui.tableWidgetAddressBook.setIconSize(QtCore.QSize(identicon_size, identicon_size)) - self.ui.tableWidgetWhitelist.setIconSize(QtCore.QSize(identicon_size, identicon_size)) + #self.ui.tableWidgetWhitelist.setIconSize(QtCore.QSize(identicon_size, identicon_size)) self.ui.tableWidgetBlacklist.setIconSize(QtCore.QSize(identicon_size, identicon_size)) self.UISignalThread = UISignaler() @@ -1819,7 +1829,7 @@ class MyForm(QtGui.QMainWindow): isEnabled = shared.config.getboolean( addressInKeysFile, 'enabled') # I realize that this is poor programming practice but I don't care. It's easier for others to read. if isEnabled: - self.ui.comboBoxSendFrom.insertItem(0, unicode(shared.config.get( + self.ui.comboBoxSendFrom.insertItem(0, identiconize(addressInKeysFile), unicode(shared.config.get( addressInKeysFile, 'label'), 'utf-8'), addressInKeysFile) self.ui.comboBoxSendFrom.insertItem(0, '', '') if(self.ui.comboBoxSendFrom.count() == 2): @@ -1851,6 +1861,7 @@ class MyForm(QtGui.QMainWindow): newItem = QtGui.QTableWidgetItem(unicode(toLabel, 'utf-8')) newItem.setToolTip(unicode(toLabel, 'utf-8')) newItem.setData(Qt.UserRole, str(toAddress)) + newItem.setIcon(identiconize(toAddress)) self.ui.tableWidgetSent.setItem(0, 0, newItem) if fromLabel == '': newItem = QtGui.QTableWidgetItem(unicode(fromAddress, 'utf-8')) @@ -1859,7 +1870,7 @@ class MyForm(QtGui.QMainWindow): newItem = QtGui.QTableWidgetItem(unicode(fromLabel, 'utf-8')) newItem.setToolTip(unicode(fromLabel, 'utf-8')) newItem.setData(Qt.UserRole, str(fromAddress)) - newItem.setIcon(identiconize(address)) + newItem.setIcon(identiconize(fromAddress)) self.ui.tableWidgetSent.setItem(0, 1, newItem) newItem = QtGui.QTableWidgetItem(unicode(subject, 'utf-8)')) newItem.setToolTip(unicode(subject, 'utf-8)')) @@ -1929,6 +1940,7 @@ class MyForm(QtGui.QMainWindow): if shared.safeConfigGetBoolean(str(toAddress), 'chan'): newItem.setTextColor(QtGui.QColor(216, 119, 0)) # orange self.ui.tableWidgetInbox.insertRow(0) + newItem.setIcon(identiconize(toAddress)) self.ui.tableWidgetInbox.setItem(0, 0, newItem) if fromLabel == '': @@ -1943,6 +1955,7 @@ class MyForm(QtGui.QMainWindow): self.notifierShow(unicode(_translate("MainWindow",'New Message').toUtf8(),'utf-8'), unicode(_translate("MainWindow",'From ').toUtf8(),'utf-8') + unicode(fromLabel, 'utf-8'), self.SOUND_KNOWN, unicode(fromLabel, 'utf-8')) newItem.setData(Qt.UserRole, str(fromAddress)) newItem.setFont(font) + newItem.setIcon(identiconize(fromAddress)) self.ui.tableWidgetInbox.setItem(0, 1, newItem) newItem = QtGui.QTableWidgetItem(unicode(subject, 'utf-8)')) newItem.setToolTip(unicode(subject, 'utf-8)')) From 9b28e1932dcb9705b82fbf89e1ddf56d316457c9 Mon Sep 17 00:00:00 2001 From: sendiulo Date: Fri, 30 Aug 2013 09:02:49 +0200 Subject: [PATCH 03/22] quoted out the BSD licensed code (as i didn't get it to work properly yet) --- src/bitmessageqt/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 784bd6f9..13101cce 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -53,7 +53,7 @@ import hashlib # load identicon code # :Author:Shin Adachi # Licesensed under FreeBSD License. -import identicon +# import identicon # usage: identicon.render_identicon(code, size) # requires PIL From a9fb87173fc707f1da284c5ed745acd0932e4b9b Mon Sep 17 00:00:00 2001 From: sendiulo Date: Wed, 4 Sep 2013 13:45:23 +0200 Subject: [PATCH 04/22] now it should also work with the other identicon file --- src/bitmessageqt/__init__.py | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 13101cce..23bbf8cd 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -53,7 +53,7 @@ import hashlib # load identicon code # :Author:Shin Adachi # Licesensed under FreeBSD License. -# import identicon +import identicon # usage: identicon.render_identicon(code, size) # requires PIL @@ -75,29 +75,27 @@ def identiconize(address): idcon = QtGui.QIcon() return idcon - suffix = "" # here you could put "@bitmessge.ch" or "@bm.addr" to make it compatible with other identicon generators - # instead, you could also use a pseudo-password to salt the generation of the identicons - # Attacks where someone creates an address to mimic someone else's identicon should be impossible then - # i think it should generate a random string by default - - # hash = hashlib.md5(addBMIfNotPresent(address)+suffix).hexdigest()[:8] - # print hash - ##japanese code - # idcon_render = identicon.render_identicon(int(hash, 16), 8) - # idcon_render.save('images/'+hash+'.png') - # im = idcon_render - # http://qt-project.org/forums/viewthread/5866 - # from PIL import Image - # from PyQt4.QtGui import QImage, QImageReader, QLabel, QPixmap, QApplication - str_broadcast_subscribers = '[Broadcast subscribers]' if address == str_broadcast_subscribers: idcon = QtGui.QIcon(":/newPrefix/images/can-icon-24px.png") return idcon + + suffix = "asdf" + # here you could put "@bitmessge.ch" or "@bm.addr" to make it compatible with other identicon generators + # instead, you could also use a pseudo-password to salt the generation of the identicons + # Attacks where someone creates an address to mimic someone else's identicon should be impossible then + # i think it should generate a random string by default - # PHP-like code - idcon_render = Pydenticon(addBMIfNotPresent(address)+suffix) - image = idcon_render._render() + if True: # identicon.py + hash = hashlib.md5(addBMIfNotPresent(address)+suffix).hexdigest() + idcon_render = identicon.render_identicon(int(hash, 16), 48) + image = idcon_render + # http://qt-project.org/forums/viewthread/5866 + # from PIL import Image + # from PyQt4.QtGui import QImage, QImageReader, QLabel, QPixmap, QApplication + else: # pydenticon.py + idcon_render = Pydenticon(addBMIfNotPresent(address)+suffix) + image = idcon_render._render() # im = Image.open('images/'+hash+'.png') # http://stackoverflow.com/questions/6756820/python-pil-image-tostring From 240e9b5b58e156d32bec2caa373e9d3889971cc1 Mon Sep 17 00:00:00 2001 From: sendiulo Date: Mon, 16 Sep 2013 21:08:55 +0200 Subject: [PATCH 05/22] Updated to work with QPixmap instead of PIL! The original source is licensed under a BSD-License, so it should be fine to use. --- src/bitmessageqt/__init__.py | 61 ++--- src/identicon_py_license | 11 - src/pydenticon.py | 409 ---------------------------- src/{identicon.py => qidenticon.py} | 122 ++++++--- 4 files changed, 119 insertions(+), 484 deletions(-) delete mode 100644 src/identicon_py_license delete mode 100644 src/pydenticon.py rename src/{identicon.py => qidenticon.py} (56%) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 23bbf8cd..1ceca0eb 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -47,20 +47,6 @@ except Exception as err: print 'Error message:', err sys.exit() -# for the md5 hash (used for the identicons) -import hashlib - -# load identicon code -# :Author:Shin Adachi -# Licesensed under FreeBSD License. -import identicon -# usage: identicon.render_identicon(code, size) -# requires PIL - -# load another identicon code -# http://boottunes.googlecode.com/svn-history/r302/trunk/src/pydenticon.py -from pydenticon import Pydenticon - try: _encoding = QtGui.QApplication.UnicodeUTF8 except AttributeError: @@ -74,7 +60,8 @@ def identiconize(address): if youdontwantidenticons == True: idcon = QtGui.QIcon() return idcon - + size = 3 + str_broadcast_subscribers = '[Broadcast subscribers]' if address == str_broadcast_subscribers: idcon = QtGui.QIcon(":/newPrefix/images/can-icon-24px.png") @@ -86,25 +73,37 @@ def identiconize(address): # Attacks where someone creates an address to mimic someone else's identicon should be impossible then # i think it should generate a random string by default - if True: # identicon.py + identicon_lib = 'qidenticon' + if identicon_lib == 'qidenticon': + # originally by: + # :Author:Shin Adachi + # Licesensed under FreeBSD License. + # stripped from PIL and uses QT instead + import qidenticon + + import hashlib hash = hashlib.md5(addBMIfNotPresent(address)+suffix).hexdigest() - idcon_render = identicon.render_identicon(int(hash, 16), 48) - image = idcon_render - # http://qt-project.org/forums/viewthread/5866 - # from PIL import Image - # from PyQt4.QtGui import QImage, QImageReader, QLabel, QPixmap, QApplication - else: # pydenticon.py + image = qidenticon.render_identicon(int(hash, 16), 48) + idcon = QtGui.QIcon() + idcon.addPixmap(image, QtGui.QIcon.Normal, QtGui.QIcon.Off) + return idcon + + elif identicon_lib == 'pydenticon': # pydenticon.py + # print identicon_lib + # load another identicon code + # http://boottunes.googlecode.com/svn-history/r302/trunk/src/pydenticon.py + from pydenticon import Pydenticon + # GPLv3 !!! idcon_render = Pydenticon(addBMIfNotPresent(address)+suffix) image = idcon_render._render() - - # im = Image.open('images/'+hash+'.png') - # http://stackoverflow.com/questions/6756820/python-pil-image-tostring - data = image.convert("RGBA").tostring("raw", "RGBA") - image = QImage(data, image.size[0], image.size[1], QImage.Format_ARGB32) - pix = QPixmap.fromImage(image) - idcon = QtGui.QIcon() - idcon.addPixmap(pix, QtGui.QIcon.Normal, QtGui.QIcon.Off) - return idcon + # im = Image.open('images/'+hash+'.png') + # http://stackoverflow.com/questions/6756820/python-pil-image-tostring + data = image.convert("RGBA").tostring("raw", "RGBA") + image = QImage(data, image.size[0], image.size[1], QImage.Format_ARGB32) + pix = QPixmap.fromImage(image) + idcon = QtGui.QIcon() + idcon.addPixmap(pix, QtGui.QIcon.Normal, QtGui.QIcon.Off) + return idcon class MyForm(QtGui.QMainWindow): diff --git a/src/identicon_py_license b/src/identicon_py_license deleted file mode 100644 index e6e964fb..00000000 --- a/src/identicon_py_license +++ /dev/null @@ -1,11 +0,0 @@ -identicon.py is Licesensed under FreeBSD License. -(http://www.freebsd.org/copyright/freebsd-license.html) - -Copyright 1994-2009 Shin Adachi. All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - - 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/src/pydenticon.py b/src/pydenticon.py deleted file mode 100644 index 57076093..00000000 --- a/src/pydenticon.py +++ /dev/null @@ -1,409 +0,0 @@ -""" -Python implementation of PHP Identicons, found here: -http://sourceforge.net/projects/identicons/ - -Licensed under the GPLv3. -""" -from PIL import Image, ImageDraw, ImagePath, ImageColor -from math import fabs as abs -from hashlib import md5 -import StringIO - -class Pydenticon: - """ - Generate an identicon. - - """ - - WHITE = (255, 255, 255) - - def __init__(self, input, size = 128): - self.hash = md5(input).hexdigest() - self.size = size - - def as_string(self): - """ - Return the image as a string representation of a PNG file. - - """ - if not hasattr(self, 'image'): - self.image = self._render() - output = StringIO.StringIO() - self.image.save(output, format="PNG") - contents = output.getvalue() - output.close() - return contents - - def save(self, filename): - """ - Save the result as a PNG file. - - """ - if not hasattr(self, 'image'): - self.image = self._render() - self.image.save(filename, format="PNG") - - def _get_sprite(self, shape, r, g, b, rotation): - """ - Generate sprite for corners and sides and returnro a PIL.Image object. - - """ - sprite = Image.new("RGB", (self.size, self.size), self.WHITE) - - if shape == 0: # triangle - points = [ - 0.5,1, - 1,0, - 1,1 - ] - elif shape == 1: #parallelogram - points = [ - 0.5,0, - 1,0, - 0.5,1, - 0,1 - ] - elif shape == 2: # mouse ears - points = [ - 0.5,0, - 1,0, - 1,1, - 0.5,1, - 1,0.5 - ] - elif shape == 3: # ribbon - points = [ - 0,0.5, - 0.5,0, - 1,0.5, - 0.5,1, - 0.5,0.5 - ] - elif shape == 4: # sails - points = [ - 0,0.5, - 1,0, - 1,1, - 0,1, - 1,0.5 - ] - elif shape == 5: # fins - points = [ - 1,0, - 1,1, - 0.5,1, - 1,0.5, - 0.5,0.5 - ] - elif shape == 6: # beak - points = [ - 0,0, - 1,0, - 1,0.5, - 0,0, - 0.5,1, - 0,1 - ] - elif shape == 7: # chevron - points = [ - 0,0, - 0.5,0, - 1,0.5, - 0.5,1, - 0,1, - 0.5,0.5 - ] - elif shape == 8: # fish - points = [ - 0.5,0, - 0.5,0.5, - 1,0.5, - 1,1, - 0.5,1, - 0.5,0.5, - 0,0.5 - ] - elif shape == 9: # kite - points = [ - 0,0, - 1,0, - 0.5,0.5, - 1,0.5, - 0.5,1, - 0.5,0.5, - 0,1 - ] - elif shape == 10: # trough - points = [ - 0,0.5, - 0.5,1, - 1,0.5, - 0.5,0, - 1,0, - 1,1, - 0,1 - ] - elif shape == 11: # rays - points = [ - 0.5,0, - 1,0, - 1,1, - 0.5,1, - 1,0.75, - 0.5,0.5, - 1,0.25 - ] - elif shape == 12: # double rhombus - points = [ - 0,0.5, - 0.5,0, - 0.5,0.5, - 1,0, - 1,0.5, - 0.5,1, - 0.5,0.5, - 0,1 - ] - elif shape == 13: # crown - points = [ - 0,0, - 1,0, - 1,1, - 0,1, - 1,0.5, - 0.5,0.25, - 0.5,0.75, - 0,0.5, - 0.5,0.25 - ] - elif shape == 14: # radioactive - points = [ - 0,0.5, - 0.5,0.5, - 0.5,0, - 1,0, - 0.5,0.5, - 1,0.5, - 0.5,1, - 0.5,0.5, - 0,1 - ] - else: # tiles - points = [ - 0,0, - 1,0, - 0.5,0.5, - 0.5,0, - 0,0.5, - 1,0.5, - 0.5,1, - 0.5,0.5, - 0,1 - ] - - # apply ratios - for i in range(0, len(points)): - points[i] = points[i] * self.size - - draw = ImageDraw.Draw(sprite) - draw.polygon(points, fill=(r, g, b)) - - for i in range(rotation): - sprite = sprite.rotate(90) - - return sprite - - def _get_center(self, shape, fR, fG, fB, bR, bG, bB, useBg): - """ - Generate sprite for center block and return a PIL.Image object. - - """ - sprite = Image.new("RGB", (self.size, self.size), self.WHITE) - - # make sure there's enough contrast before we use background color of side sprite - sufficient_contrast = ( - abs(fR - bR) > 127 or abs(fG - bG) > 127 or abs(fB - bB) > 127 - ) - if useBg > 0 and sufficient_contrast: - bg = (bR, bG, bB) - else: - bg = (255, 255, 255) - - if shape == 0: # empty - points = [] - - elif shape == 1: # fill - points = [ - 0,0, - 1,0, - 1,1, - 0,1 - ] - elif shape == 2: # diamond - points = [ - 0.5,0, - 1,0.5, - 0.5,1, - 0,0.5 - ] - elif shape == 3: # reverse diamond - points = [ - 0,0, - 1,0, - 1,1, - 0,1, - 0,0.5, - 0.5,1, - 1,0.5, - 0.5,0, - 0,0.5 - ] - elif shape == 4: # cross - points = [ - 0.25,0, - 0.75,0, - 0.5,0.5, - 1,0.25, - 1,0.75, - 0.5,0.5, - 0.75,1, - 0.25,1, - 0.5,0.5, - 0,0.75, - 0,0.25, - 0.5,0.5 - ] - elif shape == 5: # morning star - points = [ - 0,0, - 0.5,0.25, - 1,0, - 0.75,0.5, - 1,1, - 0.5,0.75, - 0,1, - 0.25,0.5 - ] - elif shape == 6: # small square - points = [ - 0.33,0.33, - 0.67,0.33, - 0.67,0.67, - 0.33,0.67 - ] - elif shape == 7: # checkerboard - points = [ - 0,0, - 0.33,0, - 0.33,0.33, - 0.66,0.33, - 0.67,0, - 1,0, - 1,0.33, - 0.67,0.33, - 0.67,0.67, - 1,0.67, - 1,1, - 0.67,1, - 0.67,0.67, - 0.33,0.67, - 0.33,1, - 0,1, - 0,0.67, - 0.33,0.67, - 0.33,0.33, - 0,0.33 - ] - - # apply ratios - for i in range(0, len(points)): - points[i] = points[i] * self.size - - if len(points) > 0: - draw = ImageDraw.Draw(sprite) - draw.polygon(points, fill=(fR, fG, fB)) - - return sprite - - def _render(self): - """ - Render the image and return the PIL.Image object. - - """ - # parse hash string - corner_sprite_shape = int(self.hash[0:1], 16) - side_sprite_shape = int(self.hash[1:2], 16) - center_sprite_shape = int(self.hash[2:3], 16) & 7 - - corner_sprite_rot = int(self.hash[3:4], 16) & 3 - side_sprite_rot = int(self.hash[4:5], 16) & 3 - center_sprite_bg = int(self.hash[5:6], 16) % 2 - - # corner sprite foreground color - corner_sprite_fg_r = int(self.hash[6:8], 16) - corner_sprite_fg_g = int(self.hash[8:10], 16) - corner_sprite_fg_b = int(self.hash[10:12], 16) - - # side sprite foreground color - side_sprite_fg_r = int(self.hash[12:14], 16) - side_sprite_fg_g = int(self.hash[14:16], 16) - side_sprite_fg_b = int(self.hash[16:18], 16) - - # final angle of rotation - angle = int(self.hash[18:20], 16) - - # start with blank 3X sized identicon - identicon = Image.new("RGB", (self.size*3, self.size*3), self.WHITE) - - # generate corner sprites - corner = self._get_sprite( - corner_sprite_shape, - corner_sprite_fg_r, - corner_sprite_fg_g, - corner_sprite_fg_b, - corner_sprite_rot - ) - identicon.paste(corner, (0, 0)) - corner = corner.rotate(90) - identicon.paste(corner, (0, self.size*2)) - corner = corner.rotate(90) - identicon.paste(corner, (self.size*2, self.size*2)) - corner = corner.rotate(90) - identicon.paste(corner, (self.size*2, 0)) - - # generate side sprites - side = self._get_sprite( - side_sprite_shape, - side_sprite_fg_r, - side_sprite_fg_g, - side_sprite_fg_b, - side_sprite_rot - ) - identicon.paste(side, (self.size, 0)) - side = side.rotate(90) - identicon.paste(side, (0, self.size)) - side = side.rotate(90) - identicon.paste(side, (self.size, self.size*2)) - side = side.rotate(90) - identicon.paste(side, (self.size*2, self.size)) - - # generate center sprite - center = self._get_center( - center_sprite_shape, - corner_sprite_fg_r, - corner_sprite_fg_g, - corner_sprite_fg_b, - side_sprite_fg_r, - side_sprite_fg_g, - side_sprite_fg_b, - center_sprite_bg - ) - identicon.paste(center, (self.size, self.size)) - - # resize image - resized = identicon.resize( - (self.size, self.size), - Image.ANTIALIAS - ) - - return resized \ No newline at end of file diff --git a/src/identicon.py b/src/qidenticon.py similarity index 56% rename from src/identicon.py rename to src/qidenticon.py index fae5a250..5d77aeb6 100644 --- a/src/identicon.py +++ b/src/qidenticon.py @@ -1,6 +1,40 @@ #!/usr/bin/env python # -*- coding:utf-8 -*- + +### +# qidenticon.py is Licesensed under FreeBSD License. +# (http://www.freebsd.org/copyright/freebsd-license.html) +# +# Copyright 2013 "Sendiulo". All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +### + +### +# identicon.py is Licesensed under FreeBSD License. +# (http://www.freebsd.org/copyright/freebsd-license.html) +# +# Copyright 1994-2009 Shin Adachi. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +### + """ +qidenticon.py +identicon python implementation with QPixmap output +by sendiulo + +based on identicon.py identicon python implementation. by Shin Adachi @@ -11,19 +45,17 @@ by Shin Adachi >>> python identicon.py [code] == python == ->>> import identicon ->>> identicon.render_identicon(code, size) +>>> import qtidenticon +>>> qtidenticon.render_identicon(code, size) Return a PIL Image class instance which have generated identicon image. ```size``` specifies `patch size`. Generated image size is 3 * ```size```. """ -# g -# PIL Modules -import Image -import ImageDraw -import ImagePath -import ImageColor +# we probably don't need all of them, but i don't want to check now +from PyQt4 import QtCore, QtGui +from PyQt4.QtCore import * +from PyQt4.QtGui import * __all__ = ['render_identicon', 'IdenticonRendererBase'] @@ -114,45 +146,46 @@ class IdenticonRendererBase(object): def render(self, size): """ - render identicon to PIL.Image + render identicon to QPixmap @param size identicon patchsize. (image size is 3 * [size]) - @return PIL.Image + @return QPixmap """ # decode the code middle, corner, side, foreColor, backColor = self.decode(self.code) - # make image - image = Image.new("RGB", (size * 3, size * 3)) - draw = ImageDraw.Draw(image) + # make image + image = QPixmap(QSize(size * 3, size * 3)) # fill background - draw.rectangle((0, 0, image.size[0], image.size[1]), fill=0) + image.fill(QtGui.QColor(0,0,0)) kwds = { - 'draw': draw, + 'image': image, 'size': size, 'foreColor': foreColor, 'backColor': backColor} + # middle patch - self.drawPatch((1, 1), middle[2], middle[1], middle[0], **kwds) - + image = self.drawPatchQt((1, 1), middle[2], middle[1], middle[0], **kwds) + # side patch kwds['type'] = side[0] for i in xrange(4): pos = [(1, 0), (2, 1), (1, 2), (0, 1)][i] - self.drawPatch(pos, side[2] + 1 + i, side[1], **kwds) - + image = self.drawPatchQt(pos, side[2] + 1 + i, side[1], **kwds) + # corner patch kwds['type'] = corner[0] for i in xrange(4): pos = [(0, 0), (2, 0), (2, 2), (0, 2)][i] - self.drawPatch(pos, corner[2] + 1 + i, corner[1], **kwds) + image = self.drawPatchQt(pos, corner[2] + 1 + i, corner[1], **kwds) return image - def drawPatch(self, pos, turn, invert, type, draw, size, foreColor, + + def drawPatchQt(self, pos, turn, invert, type, image, size, foreColor, backColor): """ @param size patch size @@ -162,24 +195,43 @@ class IdenticonRendererBase(object): # blank patch invert = not invert path = [(0., 0.), (1., 0.), (1., 1.), (0., 1.), (0., 0.)] - patch = ImagePath.Path(path) + + + polygon = QPolygonF([QPointF(x*size,y*size) for x,y in path]) + + rot = turn % 4 + rot_trans = [QPointF(0.,0.), QPointF(size, 0.), QPointF(size, size), QPointF(0., size)] + rotation = [0,90,180,270] + # print rotation[rot], rot_trans[rot], turn + pen_color = QtGui.QColor(255, 255, 255, 0) + pen = QtGui.QPen(pen_color, Qt.SolidPattern) + foreBrush = QtGui.QBrush(foreColor, Qt.SolidPattern) + backBrush = QtGui.QBrush(backColor, Qt.SolidPattern) if invert: - foreColor, backColor = backColor, foreColor + foreBrush, backBrush = backBrush, foreBrush - mat = Matrix2D.rotateSquare(turn, pivot=(0.5, 0.5)) *\ - Matrix2D.translate(*pos) *\ - Matrix2D.scale(size, size) + painter = QPainter() + painter.begin(image) + painter.setPen(pen) - patch.transform(mat.for_PIL()) - draw.rectangle((pos[0] * size, pos[1] * size, (pos[0] + 1) * size, - (pos[1] + 1) * size), fill=backColor) - draw.polygon(patch, fill=foreColor, outline=foreColor) + painter.translate(pos[0]*size, pos[1]*size) + painter.translate(rot_trans[rot]) + painter.rotate(rotation[rot]) + + painter.setBrush(backBrush) + painter.drawRect(0,0, size, size) + + painter.setBrush(foreBrush) + painter.drawPolygon(polygon, Qt.WindingFill) + + painter.end() + + return image ### virtual functions def decode(self, code): raise NotImplementedError - - + class DonRenderer(IdenticonRendererBase): """ Don Park's implementation of identicon @@ -230,11 +282,14 @@ class DonRenderer(IdenticonRendererBase): red = (code >> 27) & 0x1F foreColor = (red << 3, green << 3, blue << 3) + foreColor = QtGui.QColor(*foreColor) + + bgcolor = QtGui.QColor(255,255,255) return (middleType, middleInvert, 0),\ (cornerType, cornerInvert, cornerTurn),\ (sideType, sideInvert, sideTurn),\ - foreColor, ImageColor.getrgb('white') + foreColor, bgcolor def render_identicon(code, size, renderer=None): @@ -258,5 +313,6 @@ if __name__ == '__main__': else: code = int(code) + app = Qt.QApplication(sys.argv) icon = render_identicon(code, 24) icon.save('%08x.png' % code, 'PNG') From 0959c9c07c2534d12bdde3c62060e07c1dfad61b Mon Sep 17 00:00:00 2001 From: sendiulo Date: Tue, 17 Sep 2013 10:55:26 +0200 Subject: [PATCH 06/22] ... --- src/bitmessageqt/__init__.py | 64 ++++++----- src/qidenticon.py | 198 +++++++++++------------------------ 2 files changed, 98 insertions(+), 164 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 1ceca0eb..42ccb7ae 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -56,54 +56,68 @@ def _translate(context, text): return QtGui.QApplication.translate(context, text) def identiconize(address): - youdontwantidenticons = False - if youdontwantidenticons == True: - idcon = QtGui.QIcon() - return idcon - size = 3 + size = 1 str_broadcast_subscribers = '[Broadcast subscribers]' if address == str_broadcast_subscribers: idcon = QtGui.QIcon(":/newPrefix/images/can-icon-24px.png") return idcon - - suffix = "asdf" + try: + identiconsuffix = shared.config.get('bitmessagesettings', 'identiconsuffix') + except: + identiconsuffix = '' # here you could put "@bitmessge.ch" or "@bm.addr" to make it compatible with other identicon generators # instead, you could also use a pseudo-password to salt the generation of the identicons # Attacks where someone creates an address to mimic someone else's identicon should be impossible then # i think it should generate a random string by default - identicon_lib = 'qidenticon' - if identicon_lib == 'qidenticon': + try: + identicon_lib = shared.config.get('bitmessagesettings', 'identicon') + except: + identicon_lib = 'qidenticon' + + + if (identicon_lib[:len('qidenticon')] == 'qidenticon'): + # print identicon_lib # originally by: # :Author:Shin Adachi # Licesensed under FreeBSD License. # stripped from PIL and uses QT instead import qidenticon - import hashlib - hash = hashlib.md5(addBMIfNotPresent(address)+suffix).hexdigest() - image = qidenticon.render_identicon(int(hash, 16), 48) + hash = hashlib.md5(addBMIfNotPresent(address)+identiconsuffix).hexdigest() + use_two_colors = (identicon_lib[:len('qidenticon_two')] == 'qidenticon_two') + transparent = (identicon_lib == 'qidenticon_x') | (identicon_lib == 'qidenticon_two_x') + penwidth = 0 + pixmap = qidenticon.render_identicon(int(hash, 16), 48, use_two_colors, transparent, penwidth) idcon = QtGui.QIcon() - idcon.addPixmap(image, QtGui.QIcon.Normal, QtGui.QIcon.Off) + idcon.addPixmap(pixmap, QtGui.QIcon.Normal, QtGui.QIcon.Off) return idcon - - elif identicon_lib == 'pydenticon': # pydenticon.py + elif identicon_lib == 'pydenticon': # print identicon_lib - # load another identicon code - # http://boottunes.googlecode.com/svn-history/r302/trunk/src/pydenticon.py + # Here you could load pydenticon.py (just put it in the "src" folder of your Bitmessage source) from pydenticon import Pydenticon - # GPLv3 !!! - idcon_render = Pydenticon(addBMIfNotPresent(address)+suffix) - image = idcon_render._render() - # im = Image.open('images/'+hash+'.png') - # http://stackoverflow.com/questions/6756820/python-pil-image-tostring - data = image.convert("RGBA").tostring("raw", "RGBA") - image = QImage(data, image.size[0], image.size[1], QImage.Format_ARGB32) - pix = QPixmap.fromImage(image) + # It is not included in the source, because it is licensed under GPLv3 + # GPLv3 is a copyleft license that would influence our licensing + # Find the source here: http://boottunes.googlecode.com/svn-history/r302/trunk/src/pydenticon.py + # note that it requires PIL to be installed: http://www.pythonware.com/products/pil/ + idcon_render = Pydenticon(addBMIfNotPresent(address)+identiconsuffix, size) + rendering = idcon_render._render() + data = rendering.convert("RGBA").tostring("raw", "RGBA") + qim = QImage(data, size, size, QImage.Format_ARGB32) + pix = QPixmap.fromImage(qim) idcon = QtGui.QIcon() idcon.addPixmap(pix, QtGui.QIcon.Normal, QtGui.QIcon.Off) return idcon + elif identicon_lib == False: + idcon = QtGui.QIcon() + return idcon + else: + if identicon_lib & len(identicon_lib) > 0: + print 'Error: couldn\'t find this identicon library: ', identicon_lib + print 'Control for typos!' + idcon = QtGui.QIcon() + return idcon class MyForm(QtGui.QMainWindow): diff --git a/src/qidenticon.py b/src/qidenticon.py index 5d77aeb6..ffc25eea 100644 --- a/src/qidenticon.py +++ b/src/qidenticon.py @@ -41,9 +41,6 @@ by Shin Adachi = usage = -== commandline == ->>> python identicon.py [code] - == python == >>> import qtidenticon >>> qtidenticon.render_identicon(code, size) @@ -59,80 +56,6 @@ from PyQt4.QtGui import * __all__ = ['render_identicon', 'IdenticonRendererBase'] - -class Matrix2D(list): - """Matrix for Patch rotation""" - def __init__(self, initial=[0.] * 9): - assert isinstance(initial, list) and len(initial) == 9 - list.__init__(self, initial) - - def clear(self): - for i in xrange(9): - self[i] = 0. - - def set_identity(self): - self.clear() - for i in xrange(3): - self[i] = 1. - - def __str__(self): - return '[%s]' % ', '.join('%3.2f' % v for v in self) - - def __mul__(self, other): - r = [] - if isinstance(other, Matrix2D): - for y in xrange(3): - for x in xrange(3): - v = 0.0 - for i in xrange(3): - v += (self[i * 3 + x] * other[y * 3 + i]) - r.append(v) - else: - raise NotImplementedError - return Matrix2D(r) - - def for_PIL(self): - return self[0:6] - - @classmethod - def translate(kls, x, y): - return kls([1.0, 0.0, float(x), - 0.0, 1.0, float(y), - 0.0, 0.0, 1.0]) - - @classmethod - def scale(kls, x, y): - return kls([float(x), 0.0, 0.0, - 0.0, float(y), 0.0, - 0.0, 0.0, 1.0]) - - """ - # need `import math` - @classmethod - def rotate(kls, theta, pivot=None): - c = math.cos(theta) - s = math.sin(theta) - - matR = kls([c, -s, 0., s, c, 0., 0., 0., 1.]) - if not pivot: - return matR - return kls.translate(-pivot[0], -pivot[1]) * matR * - kls.translate(*pivot) - """ - - @classmethod - def rotateSquare(kls, theta, pivot=None): - theta = theta % 4 - c = [1., 0., -1., 0.][theta] - s = [0., 1., 0., -1.][theta] - - matR = kls([c, -s, 0., s, c, 0., 0., 0., 1.]) - if not pivot: - return matR - return kls.translate(-pivot[0], -pivot[1]) * matR * \ - kls.translate(*pivot) - - class IdenticonRendererBase(object): PATH_SET = [] @@ -144,7 +67,7 @@ class IdenticonRendererBase(object): code = int(code) self.code = code - def render(self, size): + def render(self, size, twoColor, transparent, penwidth): """ render identicon to QPixmap @@ -153,30 +76,34 @@ class IdenticonRendererBase(object): """ # decode the code - middle, corner, side, foreColor, backColor = self.decode(self.code) + middle, corner, side, foreColor, secondColor, swap_cross = self.decode(self.code, twoColor) # make image - image = QPixmap(QSize(size * 3, size * 3)) + image = QPixmap(QSize(size * 3 +penwidth, size * 3 +penwidth)) # fill background - image.fill(QtGui.QColor(0,0,0)) + backColor = QtGui.QColor(255,255,255,(not transparent) * 255) + image.fill(backColor) kwds = { 'image': image, 'size': size, - 'foreColor': foreColor, + 'foreColor': foreColor if swap_cross else secondColor, + 'penwidth': penwidth, 'backColor': backColor} # middle patch image = self.drawPatchQt((1, 1), middle[2], middle[1], middle[0], **kwds) - + # side patch + kwds['foreColor'] = foreColor kwds['type'] = side[0] for i in xrange(4): pos = [(1, 0), (2, 1), (1, 2), (0, 1)][i] image = self.drawPatchQt(pos, side[2] + 1 + i, side[1], **kwds) # corner patch + kwds['foreColor'] = secondColor kwds['type'] = corner[0] for i in xrange(4): pos = [(0, 0), (2, 0), (2, 2), (0, 2)][i] @@ -186,7 +113,7 @@ class IdenticonRendererBase(object): def drawPatchQt(self, pos, turn, invert, type, image, size, foreColor, - backColor): + backColor, penwidth): """ @param size patch size """ @@ -200,30 +127,37 @@ class IdenticonRendererBase(object): polygon = QPolygonF([QPointF(x*size,y*size) for x,y in path]) rot = turn % 4 - rot_trans = [QPointF(0.,0.), QPointF(size, 0.), QPointF(size, size), QPointF(0., size)] + rect = [QPointF(0.,0.), QPointF(size, 0.), QPointF(size, size), QPointF(0., size)] rotation = [0,90,180,270] - # print rotation[rot], rot_trans[rot], turn - pen_color = QtGui.QColor(255, 255, 255, 0) - pen = QtGui.QPen(pen_color, Qt.SolidPattern) + + nopen = QtGui.QPen(foreColor, Qt.NoPen) foreBrush = QtGui.QBrush(foreColor, Qt.SolidPattern) - backBrush = QtGui.QBrush(backColor, Qt.SolidPattern) - if invert: - foreBrush, backBrush = backBrush, foreBrush + if penwidth > 0: + pen_color = QtGui.QColor(223, 223, 223) + pen = QtGui.QPen(pen_color, Qt.SolidPattern) + pen.setWidth(penwidth) painter = QPainter() painter.begin(image) - painter.setPen(pen) + painter.setPen(nopen) - painter.translate(pos[0]*size, pos[1]*size) - painter.translate(rot_trans[rot]) + painter.translate(pos[0]*size +penwidth/2, pos[1]*size +penwidth/2) + painter.translate(rect[rot]) painter.rotate(rotation[rot]) - painter.setBrush(backBrush) - painter.drawRect(0,0, size, size) - + if invert: + # subtract the actual polygon from a rectangle to invert it + rect_polygon = QPolygonF(rect) + polygon = rect_polygon.subtracted(polygon) painter.setBrush(foreBrush) + if penwidth > 0: + # draw the borders + painter.setPen(pen) + painter.drawPolygon(polygon, Qt.WindingFill) + # draw the fill + painter.setPen(nopen) painter.drawPolygon(polygon, Qt.WindingFill) - + painter.end() return image @@ -263,56 +197,42 @@ class DonRenderer(IdenticonRendererBase): p = map(lambda vec: (vec[0] / 4.0, vec[1] / 4.0), PATH_SET[idx]) PATH_SET[idx] = p + p[:1] - def decode(self, code): - # decode the code - middleType = self.MIDDLE_PATCH_SET[code & 0x03] - middleInvert= (code >> 2) & 0x01 - cornerType = (code >> 3) & 0x0F - cornerInvert= (code >> 7) & 0x01 - cornerTurn = (code >> 8) & 0x03 - sideType = (code >> 10) & 0x0F - sideInvert = (code >> 14) & 0x01 - sideTurn = (code >> 15) & 0x03 - # bug reported by Masato Hagiwara - # http://lilyx.net/iconlang-en/ - # blue = (code >> 16) & 0x1F - # green = (code >> 21) & 0x1F - blue = (code >> 17) & 0x1F - green = (code >> 22) & 0x1F - red = (code >> 27) & 0x1F + def decode(self, code, twoColor): + # decode the code + shift = 0; middleType = (code >> shift) & 0x03 + shift += 2; middleInvert= (code >> shift) & 0x01 + shift += 1; cornerType = (code >> shift) & 0x0F + shift += 4; cornerInvert= (code >> shift) & 0x01 + shift += 1; cornerTurn = (code >> shift) & 0x03 + shift += 2; sideType = (code >> shift) & 0x0F + shift += 4; sideInvert = (code >> shift) & 0x01 + shift += 1; sideTurn = (code >> shift) & 0x03 + shift += 2; blue = (code >> shift) & 0x1F + shift += 5; green = (code >> shift) & 0x1F + shift += 5; red = (code >> shift) & 0x1F + shift += 5; second_blue = (code >> shift) & 0x1F + shift += 5; second_green= (code >> shift) & 0x1F + shift += 5; second_red = (code >> shift) & 0x1F + shift += 1; swap_cross = (code >> shift) & 0x01 + + middleType = self.MIDDLE_PATCH_SET[middleType] foreColor = (red << 3, green << 3, blue << 3) foreColor = QtGui.QColor(*foreColor) - bgcolor = QtGui.QColor(255,255,255) + if twoColor: + secondColor = (second_blue << 3, second_green << 3, second_red << 3) + secondColor = QtGui.QColor(*secondColor) + else: + secondColor = foreColor return (middleType, middleInvert, 0),\ (cornerType, cornerInvert, cornerTurn),\ (sideType, sideInvert, sideTurn),\ - foreColor, bgcolor + foreColor, secondColor, swap_cross -def render_identicon(code, size, renderer=None): +def render_identicon(code, size, twoColor=False, transparent=False, penwidth=0, renderer=None): if not renderer: renderer = DonRenderer - return renderer(code).render(size) - - -if __name__ == '__main__': - import sys - - if len(sys.argv) < 2: - print 'usage: python identicon.py [CODE]....' - raise SystemExit - - for code in sys.argv[1:]: - if code.startswith('0x') or code.startswith('0X'): - code = int(code[2:], 16) - elif code.startswith('0'): - code = int(code[1:], 8) - else: - code = int(code) - - app = Qt.QApplication(sys.argv) - icon = render_identicon(code, 24) - icon.save('%08x.png' % code, 'PNG') + return renderer(code).render(size, twoColor, transparent, penwidth) \ No newline at end of file From 86485a9b096bb5b6995f900a2e8970f074a85859 Mon Sep 17 00:00:00 2001 From: sendiulo Date: Wed, 18 Sep 2013 17:39:45 +0200 Subject: [PATCH 07/22] nonfunctional identicon settings-GUI --- src/bitmessageqt/__init__.py | 23 ++++-- src/bitmessageqt/bitmessage_icons.qrc | 5 ++ src/bitmessageqt/bitmessageui.py | 12 ++- src/bitmessageqt/settings.py | 89 ++++++++++++++------ src/bitmessageqt/settings.ui | 113 ++++++++++++++++++++++---- src/qidenticon.py | 45 ++++++---- 6 files changed, 222 insertions(+), 65 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 42ccb7ae..2a5eb10e 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -56,7 +56,7 @@ def _translate(context, text): return QtGui.QApplication.translate(context, text) def identiconize(address): - size = 1 + size = 48 str_broadcast_subscribers = '[Broadcast subscribers]' if address == str_broadcast_subscribers: @@ -71,11 +71,15 @@ def identiconize(address): # Attacks where someone creates an address to mimic someone else's identicon should be impossible then # i think it should generate a random string by default + # If you include another identicon library, please generate an + # example identicon with the following md5 hash: + # 3fd4bf901b9d4ea1394f0fb358725b28 + try: identicon_lib = shared.config.get('bitmessagesettings', 'identicon') except: - identicon_lib = 'qidenticon' - + # default to no identicons + identicon_lib = False if (identicon_lib[:len('qidenticon')] == 'qidenticon'): # print identicon_lib @@ -87,11 +91,13 @@ def identiconize(address): import hashlib hash = hashlib.md5(addBMIfNotPresent(address)+identiconsuffix).hexdigest() use_two_colors = (identicon_lib[:len('qidenticon_two')] == 'qidenticon_two') - transparent = (identicon_lib == 'qidenticon_x') | (identicon_lib == 'qidenticon_two_x') + opacity = int(not((identicon_lib == 'qidenticon_x') | (identicon_lib == 'qidenticon_two_x') | (identicon_lib == 'qidenticon_b') | (identicon_lib == 'qidenticon_two_b')))*255 penwidth = 0 - pixmap = qidenticon.render_identicon(int(hash, 16), 48, use_two_colors, transparent, penwidth) - idcon = QtGui.QIcon() - idcon.addPixmap(pixmap, QtGui.QIcon.Normal, QtGui.QIcon.Off) + image = qidenticon.render_identicon(int(hash, 16), size, use_two_colors, opacity, penwidth) + # filename = './images/identicons/'+hash+'.png' + # image.save(filename) + idcon = QIcon() + idcon.addPixmap(image, QtGui.QIcon.Normal, QtGui.QIcon.Off) return idcon elif identicon_lib == 'pydenticon': # print identicon_lib @@ -101,7 +107,7 @@ def identiconize(address): # GPLv3 is a copyleft license that would influence our licensing # Find the source here: http://boottunes.googlecode.com/svn-history/r302/trunk/src/pydenticon.py # note that it requires PIL to be installed: http://www.pythonware.com/products/pil/ - idcon_render = Pydenticon(addBMIfNotPresent(address)+identiconsuffix, size) + idcon_render = Pydenticon(addBMIfNotPresent(address)+identiconsuffix, size*3) rendering = idcon_render._render() data = rendering.convert("RGBA").tostring("raw", "RGBA") qim = QImage(data, size, size, QImage.Format_ARGB32) @@ -116,6 +122,7 @@ def identiconize(address): if identicon_lib & len(identicon_lib) > 0: print 'Error: couldn\'t find this identicon library: ', identicon_lib print 'Control for typos!' + # default to no identicons idcon = QtGui.QIcon() return idcon diff --git a/src/bitmessageqt/bitmessage_icons.qrc b/src/bitmessageqt/bitmessage_icons.qrc index bdd3fd07..68574630 100644 --- a/src/bitmessageqt/bitmessage_icons.qrc +++ b/src/bitmessageqt/bitmessage_icons.qrc @@ -1,5 +1,10 @@ + ../images/no_identicons.png + ../images/qidenticon_x.png + ../images/qidenticon.png + ../images/qidenticon_two.png + ../images/qidenticon_two_x.png ../images/can-icon-24px-yellow.png ../images/can-icon-24px-red.png ../images/can-icon-24px-green.png diff --git a/src/bitmessageqt/bitmessageui.py b/src/bitmessageqt/bitmessageui.py index c051c076..8f8b8951 100644 --- a/src/bitmessageqt/bitmessageui.py +++ b/src/bitmessageqt/bitmessageui.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'bitmessageui.ui' # -# Created: Mon Aug 12 00:08:20 2013 +# Created: Wed Sep 18 17:38:54 2013 # by: PyQt4 UI code generator 4.10.2 # # WARNING! All changes made in this file will be lost! @@ -630,3 +630,13 @@ class Ui_MainWindow(object): self.actionJoinChan.setText(_translate("MainWindow", "Join / Create chan", None)) import bitmessage_icons_rc + +if __name__ == "__main__": + import sys + app = QtGui.QApplication(sys.argv) + MainWindow = QtGui.QMainWindow() + ui = Ui_MainWindow() + ui.setupUi(MainWindow) + MainWindow.show() + sys.exit(app.exec_()) + diff --git a/src/bitmessageqt/settings.py b/src/bitmessageqt/settings.py index 821df6cf..290c0cb6 100644 --- a/src/bitmessageqt/settings.py +++ b/src/bitmessageqt/settings.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'settings.ui' # -# Created: Sun Aug 25 18:09:38 2013 +# Created: Wed Sep 18 17:38:55 2013 # by: PyQt4 UI code generator 4.10.2 # # WARNING! All changes made in this file will be lost! @@ -45,8 +45,6 @@ class Ui_settingsDialog(object): self.checkBoxMinimizeToTray.setChecked(True) self.checkBoxMinimizeToTray.setObjectName(_fromUtf8("checkBoxMinimizeToTray")) self.gridLayout_5.addWidget(self.checkBoxMinimizeToTray, 2, 0, 1, 1) - spacerItem = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) - self.gridLayout_5.addItem(spacerItem, 10, 0, 1, 1) self.checkBoxStartOnLogon = QtGui.QCheckBox(self.tabUserInterface) self.checkBoxStartOnLogon.setObjectName(_fromUtf8("checkBoxStartOnLogon")) self.gridLayout_5.addWidget(self.checkBoxStartOnLogon, 0, 0, 1, 1) @@ -82,7 +80,35 @@ class Ui_settingsDialog(object): self.languageComboBox.addItem(_fromUtf8("")) self.languageComboBox.addItem(_fromUtf8("")) self.horizontalLayout_2.addWidget(self.languageComboBox) - self.gridLayout_5.addWidget(self.groupBox, 10, 1, 1, 1) + self.gridLayout_5.addWidget(self.groupBox, 7, 1, 4, 1) + self.groupBox_3 = QtGui.QGroupBox(self.tabUserInterface) + self.groupBox_3.setObjectName(_fromUtf8("groupBox_3")) + self.comboBox = QtGui.QComboBox(self.groupBox_3) + self.comboBox.setGeometry(QtCore.QRect(20, 20, 251, 31)) + self.comboBox.setIconSize(QtCore.QSize(24, 24)) + self.comboBox.setObjectName(_fromUtf8("comboBox")) + icon = QtGui.QIcon() + icon.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/no_identicons.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.comboBox.addItem(icon, _fromUtf8("")) + icon1 = QtGui.QIcon() + icon1.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/qidenticon.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.comboBox.addItem(icon1, _fromUtf8("")) + icon2 = QtGui.QIcon() + icon2.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/qidenticon_x.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.comboBox.addItem(icon2, _fromUtf8("")) + icon3 = QtGui.QIcon() + icon3.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/qidenticon_two.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.comboBox.addItem(icon3, _fromUtf8("")) + icon4 = QtGui.QIcon() + icon4.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/qidenticon_two_x.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) + self.comboBox.addItem(icon4, _fromUtf8("")) + self.checkBoxLoadAvatars = QtGui.QCheckBox(self.groupBox_3) + self.checkBoxLoadAvatars.setGeometry(QtCore.QRect(20, 50, 121, 18)) + self.checkBoxLoadAvatars.setObjectName(_fromUtf8("checkBoxLoadAvatars")) + self.lineEdit = QtGui.QLineEdit(self.groupBox_3) + self.lineEdit.setGeometry(QtCore.QRect(140, 50, 131, 16)) + self.lineEdit.setObjectName(_fromUtf8("lineEdit")) + self.gridLayout_5.addWidget(self.groupBox_3, 7, 0, 4, 1) self.tabWidgetSettings.addTab(self.tabUserInterface, _fromUtf8("")) self.tabNetworkSettings = QtGui.QWidget() self.tabNetworkSettings.setObjectName(_fromUtf8("tabNetworkSettings")) @@ -92,8 +118,8 @@ class Ui_settingsDialog(object): self.groupBox1.setObjectName(_fromUtf8("groupBox1")) self.gridLayout_3 = QtGui.QGridLayout(self.groupBox1) self.gridLayout_3.setObjectName(_fromUtf8("gridLayout_3")) - spacerItem1 = QtGui.QSpacerItem(125, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) - self.gridLayout_3.addItem(spacerItem1, 0, 0, 1, 1) + spacerItem = QtGui.QSpacerItem(125, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) + self.gridLayout_3.addItem(spacerItem, 0, 0, 1, 1) self.label = QtGui.QLabel(self.groupBox1) self.label.setObjectName(_fromUtf8("label")) self.gridLayout_3.addWidget(self.label, 0, 1, 1, 1) @@ -150,8 +176,8 @@ class Ui_settingsDialog(object): self.checkBoxSocksListen.setObjectName(_fromUtf8("checkBoxSocksListen")) self.gridLayout_2.addWidget(self.checkBoxSocksListen, 3, 1, 1, 4) self.gridLayout_4.addWidget(self.groupBox_2, 1, 0, 1, 1) - spacerItem2 = QtGui.QSpacerItem(20, 70, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) - self.gridLayout_4.addItem(spacerItem2, 2, 0, 1, 1) + spacerItem1 = QtGui.QSpacerItem(20, 70, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) + self.gridLayout_4.addItem(spacerItem1, 2, 0, 1, 1) self.tabWidgetSettings.addTab(self.tabNetworkSettings, _fromUtf8("")) self.tab = QtGui.QWidget() self.tab.setObjectName(_fromUtf8("tab")) @@ -161,8 +187,8 @@ class Ui_settingsDialog(object): self.label_8.setWordWrap(True) self.label_8.setObjectName(_fromUtf8("label_8")) self.gridLayout_6.addWidget(self.label_8, 0, 0, 1, 3) - spacerItem3 = QtGui.QSpacerItem(203, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) - self.gridLayout_6.addItem(spacerItem3, 1, 0, 1, 1) + spacerItem2 = QtGui.QSpacerItem(203, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) + self.gridLayout_6.addItem(spacerItem2, 1, 0, 1, 1) self.label_9 = QtGui.QLabel(self.tab) self.label_9.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.label_9.setObjectName(_fromUtf8("label_9")) @@ -176,8 +202,8 @@ class Ui_settingsDialog(object): self.lineEditTotalDifficulty.setMaximumSize(QtCore.QSize(70, 16777215)) self.lineEditTotalDifficulty.setObjectName(_fromUtf8("lineEditTotalDifficulty")) self.gridLayout_6.addWidget(self.lineEditTotalDifficulty, 1, 2, 1, 1) - spacerItem4 = QtGui.QSpacerItem(203, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) - self.gridLayout_6.addItem(spacerItem4, 3, 0, 1, 1) + spacerItem3 = QtGui.QSpacerItem(203, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) + self.gridLayout_6.addItem(spacerItem3, 3, 0, 1, 1) self.label_11 = QtGui.QLabel(self.tab) self.label_11.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.label_11.setObjectName(_fromUtf8("label_11")) @@ -208,8 +234,8 @@ class Ui_settingsDialog(object): self.label_15.setWordWrap(True) self.label_15.setObjectName(_fromUtf8("label_15")) self.gridLayout_7.addWidget(self.label_15, 0, 0, 1, 3) - spacerItem5 = QtGui.QSpacerItem(102, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) - self.gridLayout_7.addItem(spacerItem5, 1, 0, 1, 1) + spacerItem4 = QtGui.QSpacerItem(102, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) + self.gridLayout_7.addItem(spacerItem4, 1, 0, 1, 1) self.label_13 = QtGui.QLabel(self.tab_2) self.label_13.setLayoutDirection(QtCore.Qt.LeftToRight) self.label_13.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) @@ -224,8 +250,8 @@ class Ui_settingsDialog(object): self.lineEditMaxAcceptableTotalDifficulty.setMaximumSize(QtCore.QSize(70, 16777215)) self.lineEditMaxAcceptableTotalDifficulty.setObjectName(_fromUtf8("lineEditMaxAcceptableTotalDifficulty")) self.gridLayout_7.addWidget(self.lineEditMaxAcceptableTotalDifficulty, 1, 2, 1, 1) - spacerItem6 = QtGui.QSpacerItem(102, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) - self.gridLayout_7.addItem(spacerItem6, 2, 0, 1, 1) + spacerItem5 = QtGui.QSpacerItem(102, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) + self.gridLayout_7.addItem(spacerItem5, 2, 0, 1, 1) self.label_14 = QtGui.QLabel(self.tab_2) self.label_14.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.label_14.setObjectName(_fromUtf8("label_14")) @@ -239,15 +265,15 @@ class Ui_settingsDialog(object): self.lineEditMaxAcceptableSmallMessageDifficulty.setMaximumSize(QtCore.QSize(70, 16777215)) self.lineEditMaxAcceptableSmallMessageDifficulty.setObjectName(_fromUtf8("lineEditMaxAcceptableSmallMessageDifficulty")) self.gridLayout_7.addWidget(self.lineEditMaxAcceptableSmallMessageDifficulty, 2, 2, 1, 1) - spacerItem7 = QtGui.QSpacerItem(20, 147, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) - self.gridLayout_7.addItem(spacerItem7, 3, 1, 1, 1) + spacerItem6 = QtGui.QSpacerItem(20, 147, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) + self.gridLayout_7.addItem(spacerItem6, 3, 1, 1, 1) self.tabWidgetSettings.addTab(self.tab_2, _fromUtf8("")) self.tabNamecoin = QtGui.QWidget() self.tabNamecoin.setObjectName(_fromUtf8("tabNamecoin")) self.gridLayout_8 = QtGui.QGridLayout(self.tabNamecoin) self.gridLayout_8.setObjectName(_fromUtf8("gridLayout_8")) - spacerItem8 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) - self.gridLayout_8.addItem(spacerItem8, 2, 0, 1, 1) + spacerItem7 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) + self.gridLayout_8.addItem(spacerItem7, 2, 0, 1, 1) self.label_16 = QtGui.QLabel(self.tabNamecoin) self.label_16.setWordWrap(True) self.label_16.setObjectName(_fromUtf8("label_16")) @@ -259,10 +285,10 @@ class Ui_settingsDialog(object): self.lineEditNamecoinHost = QtGui.QLineEdit(self.tabNamecoin) self.lineEditNamecoinHost.setObjectName(_fromUtf8("lineEditNamecoinHost")) self.gridLayout_8.addWidget(self.lineEditNamecoinHost, 2, 2, 1, 1) + spacerItem8 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) + self.gridLayout_8.addItem(spacerItem8, 3, 0, 1, 1) spacerItem9 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) - self.gridLayout_8.addItem(spacerItem9, 3, 0, 1, 1) - spacerItem10 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) - self.gridLayout_8.addItem(spacerItem10, 4, 0, 1, 1) + self.gridLayout_8.addItem(spacerItem9, 4, 0, 1, 1) self.label_18 = QtGui.QLabel(self.tabNamecoin) self.label_18.setEnabled(True) self.label_18.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) @@ -271,8 +297,8 @@ class Ui_settingsDialog(object): self.lineEditNamecoinPort = QtGui.QLineEdit(self.tabNamecoin) self.lineEditNamecoinPort.setObjectName(_fromUtf8("lineEditNamecoinPort")) self.gridLayout_8.addWidget(self.lineEditNamecoinPort, 3, 2, 1, 1) - spacerItem11 = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) - self.gridLayout_8.addItem(spacerItem11, 8, 1, 1, 1) + spacerItem10 = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) + self.gridLayout_8.addItem(spacerItem10, 8, 1, 1, 1) self.labelNamecoinUser = QtGui.QLabel(self.tabNamecoin) self.labelNamecoinUser.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.labelNamecoinUser.setObjectName(_fromUtf8("labelNamecoinUser")) @@ -280,8 +306,8 @@ class Ui_settingsDialog(object): self.lineEditNamecoinUser = QtGui.QLineEdit(self.tabNamecoin) self.lineEditNamecoinUser.setObjectName(_fromUtf8("lineEditNamecoinUser")) self.gridLayout_8.addWidget(self.lineEditNamecoinUser, 4, 2, 1, 1) - spacerItem12 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) - self.gridLayout_8.addItem(spacerItem12, 5, 0, 1, 1) + spacerItem11 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) + self.gridLayout_8.addItem(spacerItem11, 5, 0, 1, 1) self.labelNamecoinPassword = QtGui.QLabel(self.tabNamecoin) self.labelNamecoinPassword.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.labelNamecoinPassword.setObjectName(_fromUtf8("labelNamecoinPassword")) @@ -353,6 +379,14 @@ class Ui_settingsDialog(object): self.languageComboBox.setItemText(6, _translate("settingsDialog", "Russian", "ru")) self.languageComboBox.setItemText(7, _translate("settingsDialog", "Pirate English", "en_pirate")) self.languageComboBox.setItemText(8, _translate("settingsDialog", "Other (set in keys.dat)", "other")) + self.groupBox_3.setTitle(_translate("settingsDialog", "Identicons (with example image)", None)) + self.comboBox.setItemText(0, _translate("settingsDialog", "no identicons", None)) + self.comboBox.setItemText(1, _translate("settingsDialog", "qidenticon", None)) + self.comboBox.setItemText(2, _translate("settingsDialog", "qidenticon_x", None)) + self.comboBox.setItemText(3, _translate("settingsDialog", "qidenticon_two", None)) + self.comboBox.setItemText(4, _translate("settingsDialog", "qidenticon_two_x", None)) + self.checkBoxLoadAvatars.setText(_translate("settingsDialog", "Load avatar images", None)) + self.lineEdit.setToolTip(_translate("settingsDialog", "

The content of this text field will be appended to the BM-address before creating the hash for the identicons. By default it is filled with a random string to make the identicons in your client unique, otherwise the identicon could be an attack vector if an adversary creates an address resulting in a similar identicon. If you keep this string (or any other random or non-random string) you will be able to keep the same identicons.

", None)) self.tabWidgetSettings.setTabText(self.tabWidgetSettings.indexOf(self.tabUserInterface), _translate("settingsDialog", "User Interface", None)) self.groupBox1.setTitle(_translate("settingsDialog", "Listening port", None)) self.label.setText(_translate("settingsDialog", "Listen for connections on port:", None)) @@ -389,6 +423,7 @@ class Ui_settingsDialog(object): self.radioButtonNamecoinNmcontrol.setText(_translate("settingsDialog", "NMControl", None)) self.tabWidgetSettings.setTabText(self.tabWidgetSettings.indexOf(self.tabNamecoin), _translate("settingsDialog", "Namecoin integration", None)) +import bitmessage_icons_rc if __name__ == "__main__": import sys diff --git a/src/bitmessageqt/settings.ui b/src/bitmessageqt/settings.ui index eec38d8d..a5e6e4e6 100644 --- a/src/bitmessageqt/settings.ui +++ b/src/bitmessageqt/settings.ui @@ -47,19 +47,6 @@ - - - - Qt::Vertical - - - - 20 - 40 - - - - @@ -105,7 +92,7 @@ - + Interface Language @@ -163,6 +150,100 @@ + + + + Identicons (with example image) + + + + + 20 + 20 + 251 + 31 + + + + + 24 + 24 + + + + + no identicons + + + + :/newPrefix/images/no_identicons.png:/newPrefix/images/no_identicons.png + + + + + qidenticon + + + + :/newPrefix/images/qidenticon.png:/newPrefix/images/qidenticon.png + + + + + qidenticon_x + + + + :/newPrefix/images/qidenticon_x.png:/newPrefix/images/qidenticon_x.png + + + + + qidenticon_two + + + + :/newPrefix/images/qidenticon_two.png:/newPrefix/images/qidenticon_two.png + + + + + qidenticon_two_x + + + + :/newPrefix/images/qidenticon_two_x.png:/newPrefix/images/qidenticon_two_x.png + + + + + + + 20 + 50 + 121 + 18 + + + + Load avatar images + + + + + + 140 + 50 + 131 + 16 + + + + <html><head/><body><p>The content of this text field will be appended to the BM-address before creating the hash for the identicons. By default it is filled with a random string to make the identicons in your client unique, otherwise the identicon could be an attack vector if an adversary creates an address resulting in a similar identicon. If you keep this string (or any other random or non-random string) you will be able to keep the same identicons.</p></body></html> + + + + @@ -756,7 +837,9 @@ checkBoxSocksListen buttonBox - + + + buttonBox diff --git a/src/qidenticon.py b/src/qidenticon.py index ffc25eea..cc3af6b3 100644 --- a/src/qidenticon.py +++ b/src/qidenticon.py @@ -67,12 +67,12 @@ class IdenticonRendererBase(object): code = int(code) self.code = code - def render(self, size, twoColor, transparent, penwidth): + def render(self, size, twoColor, opacity, penwidth): """ - render identicon to QPixmap + render identicon to QPicture @param size identicon patchsize. (image size is 3 * [size]) - @return QPixmap + @return QPicture """ # decode the code @@ -82,7 +82,7 @@ class IdenticonRendererBase(object): image = QPixmap(QSize(size * 3 +penwidth, size * 3 +penwidth)) # fill background - backColor = QtGui.QColor(255,255,255,(not transparent) * 255) + backColor = QtGui.QColor(255,255,255,opacity) image.fill(backColor) kwds = { @@ -133,7 +133,7 @@ class IdenticonRendererBase(object): nopen = QtGui.QPen(foreColor, Qt.NoPen) foreBrush = QtGui.QBrush(foreColor, Qt.SolidPattern) if penwidth > 0: - pen_color = QtGui.QColor(223, 223, 223) + pen_color = QtGui.QColor(255, 255, 255) pen = QtGui.QPen(pen_color, Qt.SolidPattern) pen.setWidth(penwidth) @@ -147,8 +147,8 @@ class IdenticonRendererBase(object): if invert: # subtract the actual polygon from a rectangle to invert it - rect_polygon = QPolygonF(rect) - polygon = rect_polygon.subtracted(polygon) + poly_rect = QPolygonF(rect) + polygon = poly_rect.subtracted(polygon) painter.setBrush(foreBrush) if penwidth > 0: # draw the borders @@ -157,7 +157,7 @@ class IdenticonRendererBase(object): # draw the fill painter.setPen(nopen) painter.drawPolygon(polygon, Qt.WindingFill) - + painter.end() return image @@ -173,22 +173,39 @@ class DonRenderer(IdenticonRendererBase): """ PATH_SET = [ - [(0, 0), (4, 0), (4, 4), (0, 4)], # 0 + #[0] full square: + [(0, 0), (4, 0), (4, 4), (0, 4)], + #[1] right-angled triangle pointing top-left: [(0, 0), (4, 0), (0, 4)], + #[2] upwardy triangle: [(2, 0), (4, 4), (0, 4)], + #[3] left half of square, standing rectangle: [(0, 0), (2, 0), (2, 4), (0, 4)], - [(2, 0), (4, 2), (2, 4), (0, 2)], # 4 + #[4] square standing on diagonale: + [(2, 0), (4, 2), (2, 4), (0, 2)], + #[5] kite pointing topleft: [(0, 0), (4, 2), (4, 4), (2, 4)], + #[6] Sierpinski triangle, fractal triangles: [(2, 0), (4, 4), (2, 4), (3, 2), (1, 2), (2, 4), (0, 4)], + #[7] sharp angled lefttop pointing triangle: [(0, 0), (4, 2), (2, 4)], - [(1, 1), (3, 1), (3, 3), (1, 3)], # 8 + #[8] small centered square: + [(1, 1), (3, 1), (3, 3), (1, 3)], + #[9] two small triangles: [(2, 0), (4, 0), (0, 4), (0, 2), (2, 2)], + #[10] small topleft square: [(0, 0), (2, 0), (2, 2), (0, 2)], + #[11] downpointing right-angled triangle on bottom: [(0, 2), (4, 2), (2, 4)], + #[12] uppointing right-angled triangle on bottom: [(2, 2), (4, 4), (0, 4)], + #[13] small rightbottom pointing right-angled triangle on topleft: [(2, 0), (2, 2), (0, 2)], + #[14] small lefttop pointing right-angled triangle on topleft: [(0, 0), (2, 0), (0, 2)], - []] # 15 + #[15] empty: + []] + # get the [0] full square, [4] square standing on diagonale, [8] small centered square, or [15] empty tile: MIDDLE_PATCH_SET = [0, 4, 8, 15] # modify path set @@ -232,7 +249,7 @@ class DonRenderer(IdenticonRendererBase): foreColor, secondColor, swap_cross -def render_identicon(code, size, twoColor=False, transparent=False, penwidth=0, renderer=None): +def render_identicon(code, size, twoColor=False, opacity=255, penwidth=0, renderer=None): if not renderer: renderer = DonRenderer - return renderer(code).render(size, twoColor, transparent, penwidth) \ No newline at end of file + return renderer(code).render(size, twoColor, opacity, penwidth) \ No newline at end of file From 31affd438f132177b98e58498c01078898a93d95 Mon Sep 17 00:00:00 2001 From: sendiulo Date: Thu, 19 Sep 2013 21:28:22 +0200 Subject: [PATCH 08/22] avatarize --- src/bitmessageqt/__init__.py | 64 +++++++++++++++++++++++--------- src/images/can-icon-24px_2.png | Bin 0 -> 1852 bytes src/images/no_identicons.png | Bin 0 -> 197 bytes src/images/qidenticon.png | Bin 0 -> 1649 bytes src/images/qidenticon_two.png | Bin 0 -> 1737 bytes src/images/qidenticon_two_x.png | Bin 0 -> 1826 bytes src/images/qidenticon_x.png | Bin 0 -> 1763 bytes 7 files changed, 47 insertions(+), 17 deletions(-) create mode 100644 src/images/can-icon-24px_2.png create mode 100644 src/images/no_identicons.png create mode 100644 src/images/qidenticon.png create mode 100644 src/images/qidenticon_two.png create mode 100644 src/images/qidenticon_two_x.png create mode 100644 src/images/qidenticon_x.png diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 2a5eb10e..cecb6401 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -58,14 +58,6 @@ def _translate(context, text): def identiconize(address): size = 48 - str_broadcast_subscribers = '[Broadcast subscribers]' - if address == str_broadcast_subscribers: - idcon = QtGui.QIcon(":/newPrefix/images/can-icon-24px.png") - return idcon - try: - identiconsuffix = shared.config.get('bitmessagesettings', 'identiconsuffix') - except: - identiconsuffix = '' # here you could put "@bitmessge.ch" or "@bm.addr" to make it compatible with other identicon generators # instead, you could also use a pseudo-password to salt the generation of the identicons # Attacks where someone creates an address to mimic someone else's identicon should be impossible then @@ -81,13 +73,18 @@ def identiconize(address): # default to no identicons identicon_lib = False + try: + identiconsuffix = shared.config.get('bitmessagesettings', 'identiconsuffix') + except: + identiconsuffix = '' + if (identicon_lib[:len('qidenticon')] == 'qidenticon'): # print identicon_lib # originally by: # :Author:Shin Adachi # Licesensed under FreeBSD License. - # stripped from PIL and uses QT instead - import qidenticon + # stripped from PIL and uses QT instead (by sendiulo, same license) + import qidenticon import hashlib hash = hashlib.md5(addBMIfNotPresent(address)+identiconsuffix).hexdigest() use_two_colors = (identicon_lib[:len('qidenticon_two')] == 'qidenticon_two') @@ -125,6 +122,36 @@ def identiconize(address): # default to no identicons idcon = QtGui.QIcon() return idcon + +def avatarize(address, fallBackToIdenticon = False): + str_broadcast_subscribers = '[Broadcast subscribers]' + # don't add the suffix for [Broadcast subscribers] + import hashlib + hash = hashlib.md5(addBMIfNotPresent(address)).hexdigest() + if address == str_broadcast_subscribers: + hash = address + print address, ' => ', hash + idcon = QtGui.QIcon() + # http://pyqt.sourceforge.net/Docs/PyQt4/qimagereader.html#supportedImageFormats + # print QImageReader.supportedImageFormats () + # QImageReader.supportedImageFormats () + extensions = ['PNG', 'GIF', 'JPG', 'JPEG', 'SVG', 'BMP', 'MNG', 'PBM', 'PGM', 'PPM', 'TIFF', 'XBM', 'XPM', 'TGA'] + for ext in extensions: + upper = shared.appdata + 'avatars/' + hash + '.' + ext.upper() + lower = shared.appdata + 'avatars/' + hash + '.' + ext.lower() + if os.path.isfile(lower): + print 'found avatar of ', address + idcon.addFile(upper) + return idcon + elif os.path.isfile(upper): + print 'found avatar of ', address + idcon.addFile(upper) + return idcon + if fallBackToIdenticon: + return identiconize(address) + else: + return idcon + #identiconize(address) class MyForm(QtGui.QMainWindow): @@ -387,6 +414,7 @@ class MyForm(QtGui.QMainWindow): if not isEnabled: newItem.setTextColor(QtGui.QColor(128, 128, 128)) self.ui.tableWidgetYourIdentities.insertRow(0) + newItem.setIcon(avatarize(addressInKeysFile)) self.ui.tableWidgetYourIdentities.setItem(0, 0, newItem) newItem = QtGui.QTableWidgetItem(addressInKeysFile) newItem.setFlags( @@ -427,6 +455,7 @@ class MyForm(QtGui.QMainWindow): self.ui.tableWidgetAddressBook.insertRow(0) # address book item newItem = QtGui.QTableWidgetItem(unicode(label, 'utf-8')) + newItem.setIcon(avatarize(addressInKeysFile)) self.ui.tableWidgetAddressBook.setItem(0, 0, newItem) newItem = QtGui.QTableWidgetItem(address) newItem.setIcon(identiconize(address)) @@ -690,7 +719,7 @@ class MyForm(QtGui.QMainWindow): else: newItem = QtGui.QTableWidgetItem(unicode(toLabel, 'utf-8')) newItem.setToolTip(unicode(toLabel, 'utf-8')) - newItem.setIcon(identiconize(toAddress)) + newItem.setIcon(avatarize(toAddress, True)) newItem.setData(Qt.UserRole, str(toAddress)) newItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) @@ -702,7 +731,7 @@ class MyForm(QtGui.QMainWindow): else: newItem = QtGui.QTableWidgetItem(unicode(fromLabel, 'utf-8')) newItem.setToolTip(unicode(fromLabel, 'utf-8')) - newItem.setIcon(identiconize(fromAddress)) + newItem.setIcon(avatarize(fromAddress, True)) newItem.setData(Qt.UserRole, str(fromAddress)) newItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) @@ -847,7 +876,7 @@ class MyForm(QtGui.QMainWindow): newItem.setTextColor(QtGui.QColor(137, 04, 177)) if shared.safeConfigGetBoolean(str(toAddress), 'chan'): newItem.setTextColor(QtGui.QColor(216, 119, 0)) # orange - newItem.setIcon(identiconize(toAddress)) + newItem.setIcon(avatarize(toAddress, True)) self.ui.tableWidgetInbox.setItem(0, 0, newItem) # from if fromLabel == '': @@ -862,7 +891,7 @@ class MyForm(QtGui.QMainWindow): if not read: newItem.setFont(font) newItem.setData(Qt.UserRole, str(fromAddress)) - newItem.setIcon(identiconize(fromAddress)) + newItem.setIcon(avatarize(fromAddress, True)) self.ui.tableWidgetInbox.setItem(0, 1, newItem) # subject newItem = QtGui.QTableWidgetItem(unicode(subject, 'utf-8')) @@ -1565,7 +1594,7 @@ class MyForm(QtGui.QMainWindow): self.ui.tableWidgetInbox.item( i, 0).setText(unicode(toLabel, 'utf-8')) self.ui.tableWidgetInbox.item( - i, 0).setIcon(identiconize(toAddress)) + i, 0).setIcon(avatarize(toAddress, True)) # Set the color according to whether it is the address of a mailing # list or not. if shared.safeConfigGetBoolean(toAddress, 'mailinglist'): @@ -1587,7 +1616,7 @@ class MyForm(QtGui.QMainWindow): self.ui.tableWidgetSent.item( i, 1).setText(unicode(fromLabel, 'utf-8')) self.ui.tableWidgetSent.item( - i, 0).setIcon(identiconize(fromAddress)) + i, 0).setIcon(avatarize(fromAddress, True)) def rerenderSentToLabels(self): for i in range(self.ui.tableWidgetSent.rowCount()): @@ -1622,13 +1651,14 @@ class MyForm(QtGui.QMainWindow): newItem = QtGui.QTableWidgetItem(unicode(label, 'utf-8')) if not enabled: newItem.setTextColor(QtGui.QColor(128, 128, 128)) + newItem.setIcon(avatarize(address)) self.ui.tableWidgetSubscriptions.setItem(0, 0, newItem) newItem = QtGui.QTableWidgetItem(address) - newItem.setIcon(identiconize(address)) newItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) if not enabled: newItem.setTextColor(QtGui.QColor(128, 128, 128)) + newItem.setIcon(identiconize(address)) self.ui.tableWidgetSubscriptions.setItem(0, 1, newItem) def click_pushButtonSend(self): diff --git a/src/images/can-icon-24px_2.png b/src/images/can-icon-24px_2.png new file mode 100644 index 0000000000000000000000000000000000000000..30f7313eb378b69c4e2f6cb66264c2ae678059e1 GIT binary patch literal 1852 zcmV-C2gCS@P)M^!E?E!Z`=4*Xy`ht_>fhjLl+eV_hy>=-Ek(A(EP8wh@6>5>i{Jah=UrfpOJpyyEE@4x%__we9`O^8Gz zY+YB^0D>Su5CjlH;N;0u?*~Bu&N&bS0ppzGa8EB4rY4DVPDBKSF-HCU16P-HEJ7j{ zNB_V8#>OVLvurL0*LBHtT@Xo}Jb4<4WQ?)(U)V^qv$OC6A8)<&J1DB6+N!HAqgS40M1-^F1`!DUX8C&gnRGe@ zugOKk3q#X23}v$zA1@%4ZpGc13}Zxu2iAXy3X>BMLSomhm+{n7Pk?idful#^*Ezyr z?^5Ynsk5uAtC7-^+n#tfol2v%r3J6O`Z`vx?m{NB3ZCb|_3ZsV_dC<+bR%^l!h!ug z@VU>%#>bmZoH+5fD_6!g$w1(jJD&Yfx-GS5)rys1j5Ug#pWXb-IOmw2o?hS6(|2Sf zpMOr0oWEy8bh}FcG{zVdMbZCv^7OGpGT!ZY?yDjYR4SFe34j4u5I6t=RaNovCkHF@ zoiaipD2jq)GP(ESrQv7p6jU1RJ^w~D8qrNF1lzV@nHGW|z_EWH$MA4|HvlM%F)+q3 zSF1lXI(qr%k!S>tSt5A%%qNy7fY#@OJ=EhAw1( z_N}dtZ{57*k%%G? zyxTA|2q7B+k`g<9wZry251!{C8jT8 z-p;x=cW;j;6M^e_Sar{;s;=ulO|_=rc^-)9w)un*3;rOYd80A9^_8lsC>CeFcKY<0 z4{lZ}Ry-a%wttgbW>)Wx=%_7={5wQBW?I>Bfy4Z=_PG=NJe~Q9lP@w~J_0!8!Mi z9zFhP-S^kT<8fHF3CFQv7zRw!gk#&#H626*-}f;&HHG}>D5{mY;+HpXy5F+Q%Xe-M z0U*X0G)+UPbnTIkKRHlVHEm6MdpjbL2t3b)X_;^w2d-^HS2Yk3s?{on^CK7@$wLGZ zp64ZohO$2d01W`9rl;?qQ0VgL*oRY7S08R~Z--+$Fw77_rVcY?z%ng_LLsP%0zrgw zxr|)yA}WL9CMO3t=b?@zi(yy>Of!T~C+VOB*94LhM2&|}VG20s7@sU4HnuD?>4oOphsFb>PI zZUf%`%U?g-yLaC=7-Oi_YVf%apL0|y71Zl>Y~1)DcJ6!;YuDTZB0_HPA}(DTUI=X2 zCW^)4PlABI9SVie+1UxpvTpCezI`9Hzx&R+{}~^jXqkryAs~c+loA9&&G%88o5S~i zunk+bJl6RAN16Z-N~LQLgR%WmN-SO4fpAk3q?Bk*BpZN(hk5|U(0jP|^`65=ej%iQ zs;ZDuEGZ;^06r_>FlqtwW!jai!%a;Br5)J=X qQ5iHQ#>b literal 0 HcmV?d00001 diff --git a/src/images/no_identicons.png b/src/images/no_identicons.png new file mode 100644 index 0000000000000000000000000000000000000000..513f1d10ac78407b11c593756bd08ab3ff3cca03 GIT binary patch literal 197 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q4M;wBd$a>cu@pObhHwBu4M$1`kk47*5n0T@ zz;^_M8K-LVNdpDbJzX3_D(1XBXvhfU9a`|!|7|Ac0w9yB-~pSOLn6$5Jhp02g!1>on(F9 zPO=;i=>cgh3!!g)UX}G`-LXN`)#W1#xO~0;EX#7ce=ZpL{`l2yLL8{#TsB}^pjxKg zghvp{vKQpCu4_~sg!XL#!KPb~s0i1yPat61+5m!0KF1Be-9Inux+ed&e}cVTby377 zUxW;fgki^TB4B$F+cF!tRp8IC{71ttAVe6O%!XIizhKyLi3r%7*;cb5d)Xg`Rem%2 zzIp_(@oj8J!5a)at_cB~QQMjt9M<{Cu+{|~7pEI0Y_uCVSm92?jx9jI25wtl!=l2z z<^SB&8jwmMbe$-K#UU^(jJj=MU2gYJ^*%J%fMX>Sz{a;z1zWPsiaSZBl@c2kMhsi! zC8roRsud+mmj*_-GhxNnadOOxO`uOO8Wx5eTX$)gu#qqDtX8MMIF8sP*hpGMh)wta zDP&lfZfs1wxO=gDs4~UL0b@NaZoUM(mX7N&?ztTdI_)W@0&z+|pHlh{;x zaR!_EHTafz@b?(`Ukh2UW9p;WV={@y5<3<_8<38?K#oZ+HNfP9P$`wM?3xX)0w!O< z+x=4u8_v%T$ta&C=3{gWurHMzw04)MxSJBN4cw|Q(1wMvG6vX}`X1SE(t&U;KxmsG ztHPKuEDY=zU|*WLVB2Xuf@FI-Y%^+A7?XyD!5IVWOKYENzHo+;Y){5E2UdkKZ&;Yf zHhfMUy}xH@3RAK@;LRJ1E5jBGR_*EQSe=8vfW6q+vFa=y4713oyT|wcyn-Ovi&z!L zs$r%%jr8@QA23xUdqJzhSTf89r@>x6Iz>S24tpQ8#)EE{Gfw0En_vJWXpscb zFt40QdiK?8b-V`AFtm)DL3np0*+f!=fe)t8-i$gJ8eooTjfa*u%LpX*9BAJb$DWlGTxN#7^x&)soD~?M5IGpveAU6~};8Br|-=ELvpv zNj$p9F=lzmj6L=NHe%$29uDRhwy2gS$Bb;Ji2JYE{0IDw`EpbBvi~UU++I8g`V?7 zWm0}qdQH62B7AN8LbL}$eDO9yI9_c_!k%E}uQR>CkHpikM`T_YJ-CEX!+hukZdI69 zH%!2)D1%*m$S~7-8L}$OD;g$mRg{sA-fx&iy^LBF=2Z++w<=0^ckeXJkX}%n#^c;D zWLk0)N%8gCIr)31l{dqZ$0}HC9j$6UhGA~`QnB~jT8_MvtceH1n$7^xY-?>yn_*C! zx(w)I*EXw$kz7}S*w)mB2E!1xZDG*DmTi^{V;!x~wXJ^DwT9{HCD#4%Z9_LqWeiZo zw#qeD8K$C_dN&6-W=8=vOr4fWYPv`nm}Eyo8w1>S$WXlp0f&8K!dal))z4 z9ujOx$r8W@SED{I5Vc>iJsh@CjwPav;w%8YXBjKY5q4}?Uw1&LOENgWZ&BG6-<00E zM9dq3lR?0?Wu(YYhBYk+XZtJT!?q{GDigD!7`wJT49kB< z_!3w$2-vpNAh;GkinBdTv1QwzVaX3Ej#g*|0o#@{BG=^KaM~>#eA_PTx`zL&-d2RU vH(S(8u_vs_hogT`N00000NkvXXu0mjfXoLD% literal 0 HcmV?d00001 diff --git a/src/images/qidenticon_two.png b/src/images/qidenticon_two.png new file mode 100644 index 0000000000000000000000000000000000000000..351806593fabc7add7f1f23038216bf4dc788d2c GIT binary patch literal 1737 zcmV;)1~&PLP)J9^VV6h_B`lG=Xy>B#2$+|J|26J#qQVNFCm1({V`zh zc=u*~2y>oK57vk9!PEQK{q(DVp>3QUKy9{+fC3qDD3*42-wl!7ZzlD#&wr?8W0Rd%g zI)!DBhGMTVb@21pbfmX3RU51Vn1#Fxaer=4u~C`t-lGz$zdog#=N#TujkW) zd2K{b*E6{w>~Mc1K8%UBMK^{ol0vUH%!S3MX-oQ0*2bl3aq5)mG#{L7T!V zE?8I$ySD0i7t+SAYHhAog)v4p!J>_2t{Jr{zc*GYET&o;dlp1(a;i2aXG@qwWD`Z& zL>Hb4ZHAvX4GN1%YEye_m#Ixb)%KG(Axt!~iA8NDAD%X}f$ZZL7Zx*VGvA(=Y134d zTqEXti4{Ze0`f!-|FwPp^TYZ;E=UX02VwX2@$OAO^qe-bUJ`XyvKYfXK=T*mZGhIW zYm~Lws>)n?a)s$fHld&mys8-J!eWH9fvC!VXICK1FtQ0oZ6K;jX{N9kqBb;DMIW5X zgds#WQK1b@RRv8J7L%zBR#oY~M6oca$R;+l!K$jG`NCpm+VE9XZ2Q%0O(C2!x&gw@ zmK?4M3oTSNQTM#B6b87GHKdAyhr(J^RlTg#3qy#kMO8#vEv#u(ly_|;VJ49^t%^=d zh51lL{7^&{rW2VDRV<(v=1diR&w)XhP-MCY$uXB-XrIFiDnsxN0?(mSR%;bmu`tID=qOkezp#p9!6uQF33ELA zbaeb_T)N1yX8p(tggKsfI%?*WE)M2cwsK^-XH9XPIn*K2PjhPw#4~?eMC}zQYYpRC zf%-~DCoQboQh@($eb<&mC$faF?nwcut*I_Ai9%#s!nz>^WdC`kD@-CC*{ZPaMgfge z1iHv1qLD2N>sA!dJI$@@Od=QAg0SvG0qs+@yVN8?k+`Mv%B73C>?DkleF}ZXRp4^A`}z4d%S1C? zl=Su63)fI|7_S4#^7q=K*@EKv^zih~IU0SCej@2I5-;yh?_VJ<((EiR@Rhh1c1A%J zJu<2=A9;aS6?5x_>8m0}MiSO^UJX?hbBl$UtD;0!FRaD9%vI63mBP?eRYz7Stf9Qn zR1s0Sihvc!w(6erHNx=onmj$;y*ayjfwwhmF)6P9tA?G5m8@78Y+gwpu-c07P*{-z zG;O7kl?g-3t9stiwB^%kVJveEqOB;h0%0I|vCl7vwoFW(3V`u62cS?F}czzYuk7rHEkPM7FM)}rl2izaA|GZ zk!=Z+JErPWP=vHq3}{uGc473B%LA)6;vhoWHX>UU#(oeis$z)R7&FzZO`|Y&RWTLX zgcd?xZEKM&3*&lJQB^US+7#YuO4lY;n3}4X4Q+Po@C&wZ8r=<{QJ@oh2E@7fS_%JGRYT( z6%7Pi)fzr(yC^Jk2!+p8(jaIXDV_6kVLA6~uBtWKt?gXc_I;E;0&9YxZ6rs*K6z6& zRjtuhZ6Ad#-pFvb!U_a!BWp(Xi$8N;)f)J@dfjH{QCeYRmtsp59w?I_&T&v}`+F$JD<^AbKDWnwI16v9t`u>6Q;whFEZS(Yb z{WvcOgY8;4q}i(5ra+g|^=_e+0%Vc)B#DWHERsgjizQGFl1MF5Ad18-&R&gn1>#Df z6ityvBtQ@eVQi_EKsk~kQ5Ic8BuQ~f^(YD?kwPh)B5@X3LL`L5$$?3r97>Ti=5#3U zsU=QL-&YAFokA%iA|WhKjI;qUadKc2D5pgvq$i{vkr2eGeZy7@q?tk~dmRK4Zg{462{92+&KmPpGZpsi%0-<7U;q&?N?S`6~ zts&9;5r#4r%0ZYJE`i*(Ud)bxwm=jKsV#f-3{koSLQSC*1d*(WGs1KUl%pY%MR9r` zkh%r3kU}XEB3Tq?l<5;FheIUm;*3ns3<+c{g;FR)vMvq+_yo#X6KPg)D3+#3mNF36$VRcYJfejs&RLnM%k^9jdv3yVM~DdghtGi3s~ zIQMW(_Oc12kwPwxJX0i)i?a{sQqu!x=l}k0f8UdJet-WJLO8Qs-_)+oS>al^jkV>x zKKsT(U)3(d{U+wTKC3`3j~-G|ifnCmk;+(2AvS?r8a*Us6gynOg(_n)g;)e~S@e*W zQ1o~g7psi56e1VMCDB7eKJf#lT(B~hQixO_mqQON=~RxmbJ5CJNg*j1#$`WK#)uQpm7(l41y?I3<>1L*8@c=^#lsR zMXUiun4E5byy$u$$pn!=A-Iq=Ac+&{+Lgh*i@>piZ|iY}m||z@(LnkHXdVM`k5zmu zR0io&qe&o_XI?}f<6R_uP*SK`AeUxdRF*PaD18u8s7fH0WnNVOox{b_M>~az0=Xpf z0`V`cE|@-=DYPq)%P}ud+j?9yeY8?&Qy`aOULdzNyKwqwq|mBBF2lS)AMfJg=_8#& zcLKQt^J3zNI~PzN$rSn`kQaYmOda#9e4iR;|2$Zi$1<-U=jBJ>?<39{`^G{Aq(9PM-k)yfzI4~q z=bjnIf}TJEAc(`2LM#G#`h0>Q4mX}a44{g`kwWAG9XWka#o@#ghyY9CP^S>7Ku1d- zOX6_h3DkjgaVS%WOrRsAk9Bc4fH|cVo!R7}dc%_lG;8{#(mX%D-LwxfK*YyDE9|9B zlxT)Tq8JMe3p7*uv`8_NNF#Uxg)s@Ni!_=-LjqY(AIhRz7pa#+CJ<+lEs8XfLfryc zOdrXjS`?{=LnM%95v_>Sn?hXzSxFzHqFE8Cl_?Vlxk$(&^`ua*2)wT3FokWJw-mS_7g% z(w4DMwLl0jtmU1GC{j&ZkVLA%L;{UOJc39)_tqAvHifDL(s~h;V*?G5DDI^#QVGZe z;v7^;h{SPi4UuY6s3?$RgC(CvI7E_2H?%~8NFdF}SPGFe##d4zl~QO|AnwK?&42WW zgz$u?ok%JPgd|Q*jYtUMtEJHicr*at)46bX*`Zgb5xP?+TL}-$PF|;rnFb!k0ug}KnU|V z+8jxwwBBD{bnC?s2F54?MUEiSus{gnM9>6^%q~)N;NOd^`9maeBB%mIW)-PPAVhH@ z=mJG%6zOuhF1yp;$l^q-2o#w~q)mY=h!e3UP-GU7mh=af#EDoHC}Lftzo^XKM|QZg Q>Hq)$07*qoM6N<$f^(E5=l}o! literal 0 HcmV?d00001 diff --git a/src/images/qidenticon_x.png b/src/images/qidenticon_x.png new file mode 100644 index 0000000000000000000000000000000000000000..07e903e5837b35e6a8e3e8322c2b41171e9d4fcc GIT binary patch literal 1763 zcmV<91|0c`P)#(-4VoeXSbp=#UX ziEaa(|7o=_4Umn5rfH8>x#nMmkBE%c=w1+wgren4|4$pJ{32^08A)5iQ7z8(z7;+z zthNZVAQ(wYJBq$0GtkwSTSC)F(weQbwXF5Ba0)TQNc~7@#nG>92D&?-6(o(str1mQ zuIjukoLWGOMv^iSmo^f8_?PkkH7qUKUab!zOtyRBsP(Sjs-4Lx8lsVEm+V`gS%FFm zjs6l#pc14+nz?|6lt|pQ)cUB|dDiCvH7J&EG|yNO`bI5*N+c7B<1AA$k&w<1yE@Cu zwGoM{?3~L%i2VSQKqZukq%mSCkEu1^>d~HcWKl9>B+d(Hlr`LazXi=<6R2b)k+g+B zGm}UN_4f{%p^=5qu*_8hx$2{wkJau!N7wfXvj)mYTqQ=D&`U7V-alMlECq5$)Iij7 zh9Lu?Vy!biKfb-l;dsI_H~jWdgxv#?$eC!|QV)R-B7Q$JzoBRwUI zMDmz8CGHZa#QRzm6|;kFdJ?TYx<`>nq#Y8c#C-ylcv`EXVr5`0oyJ>Bp)(YTv}59w zY?nYKR@bViSRF*m7ZNR}(3ze@io|&b{$}nTc3Z2Wf9j-ymc2m99ZX019SxoHB7p%cKnEys62P?Ro16jk$CYx8^ z&;DQ`${86l5*^L;-K+)*^XMUEN|8sK!$@tcrjV=C_}~XgGm34l5Qb`FF@;<&W$cKw z2}QSe31hXfmO@+I&fy&m^NDXT6$Wc#DTTJarPUK!rc>GCE{xX3N(#keJr2+`o7yIu zVYoIHQYap6HjcK*RJZvJCs#V`owQr~Di4A=(2D0ghT#hrCfOr@ScDTNVx zKru|oHrxDq0?AAe36xS8vIivNWTxJ=W%Vh7fFAajwFSP3=fVtv^a}v@zOa}JwL$vT zfO_R+m`4xM)A%rwJ}4>FZJ;oXUsRSd!ch7kq)?ZE!YqDKy`3YBrH^(BH4PLd@e9N) ztzj^IG*hT*pfHDDpdRZ9qv@lSLbnDAQ}_k)(dICmJ{l=>X`nEJU!b>l3FGM_okDL0 z3KRIn#1?m9Kz$@r=$C<_^ZjCKo8K^^K2j<4zk#CD{bKS}2VqEkBvR;4O3~S#;Wp9n z`SJD5*}>ZPs|38n8w(YAz8)eEc5(&;%(L-3@q9fF$)9yjld?UT8tL<9IR>MLU+~ zKwiX5Y3yezx7*uK+bEGhY3;1adLpgXzLY?gg-eMxJ$6l`Efa|II*!FeTB&zBfvky? zl59Hcl1N)5kmi+RD~U8)<5U7!5F{n6(eE{pTqY3mS`~64&D1rVK&XvR$=2vb)^P+k z31rDU$c!3$$UtbL#`;=S{;9QW{-MHAI;b>G>oY5Gw7FIrqQ*74=RgPzv%Xf;Tt#tA zj;X1241#g)knS_l3tAk)d!3`LLamFcfjpd*STfHU?tYw}IL;oB8nn||RJbmPvKYwA z{(m)gHB#B`HxX72pdqUE^^o0+#9|=t*Fk#N$w=q6{%Z8-mm!FTVxSCyCNqraxNIOK zk Date: Fri, 20 Sep 2013 14:30:53 +0200 Subject: [PATCH 09/22] load avatar from file --- src/bitmessageqt/__init__.py | 66 ++++++++++++++++++++++++++++++------ 1 file changed, 55 insertions(+), 11 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index cecb6401..61b93e2b 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -124,13 +124,13 @@ def identiconize(address): return idcon def avatarize(address, fallBackToIdenticon = False): - str_broadcast_subscribers = '[Broadcast subscribers]' - # don't add the suffix for [Broadcast subscribers] import hashlib hash = hashlib.md5(addBMIfNotPresent(address)).hexdigest() + str_broadcast_subscribers = '[Broadcast subscribers]' if address == str_broadcast_subscribers: + # don't hash [Broadcast subscribers] hash = address - print address, ' => ', hash + # print address, ' => ', hash idcon = QtGui.QIcon() # http://pyqt.sourceforge.net/Docs/PyQt4/qimagereader.html#supportedImageFormats # print QImageReader.supportedImageFormats () @@ -140,11 +140,11 @@ def avatarize(address, fallBackToIdenticon = False): upper = shared.appdata + 'avatars/' + hash + '.' + ext.upper() lower = shared.appdata + 'avatars/' + hash + '.' + ext.lower() if os.path.isfile(lower): - print 'found avatar of ', address + # print 'found avatar of ', address idcon.addFile(upper) return idcon elif os.path.isfile(upper): - print 'found avatar of ', address + # print 'found avatar of ', address idcon.addFile(upper) return idcon if fallBackToIdenticon: @@ -292,6 +292,8 @@ class MyForm(QtGui.QMainWindow): "MainWindow", "Enable"), self.on_action_YourIdentitiesEnable) self.actionDisable = self.ui.addressContextMenuToolbar.addAction(_translate( "MainWindow", "Disable"), self.on_action_YourIdentitiesDisable) + self.actionSetAvatar = self.ui.addressContextMenuToolbar.addAction(_translate( + "MainWindow", "Set avatar..."), self.on_action_YourIdentitiesSetAvatar) self.actionClipboard = self.ui.addressContextMenuToolbar.addAction(_translate( "MainWindow", "Copy address to clipboard"), self.on_action_YourIdentitiesClipboard) self.actionSpecialAddressBehavior = self.ui.addressContextMenuToolbar.addAction(_translate( @@ -307,6 +309,7 @@ class MyForm(QtGui.QMainWindow): self.popMenu.addSeparator() self.popMenu.addAction(self.actionEnable) self.popMenu.addAction(self.actionDisable) + self.popMenu.addAction(self.actionSetAvatar) self.popMenu.addAction(self.actionSpecialAddressBehavior) # Popup menu for the Address Book page @@ -455,7 +458,7 @@ class MyForm(QtGui.QMainWindow): self.ui.tableWidgetAddressBook.insertRow(0) # address book item newItem = QtGui.QTableWidgetItem(unicode(label, 'utf-8')) - newItem.setIcon(avatarize(addressInKeysFile)) + newItem.setIcon(avatarize(address)) self.ui.tableWidgetAddressBook.setItem(0, 0, newItem) newItem = QtGui.QTableWidgetItem(address) newItem.setIcon(identiconize(address)) @@ -1877,7 +1880,7 @@ class MyForm(QtGui.QMainWindow): isEnabled = shared.config.getboolean( addressInKeysFile, 'enabled') # I realize that this is poor programming practice but I don't care. It's easier for others to read. if isEnabled: - self.ui.comboBoxSendFrom.insertItem(0, identiconize(addressInKeysFile), unicode(shared.config.get( + self.ui.comboBoxSendFrom.insertItem(0, avatarize(addressInKeysFile, True), unicode(shared.config.get( addressInKeysFile, 'label'), 'utf-8'), addressInKeysFile) self.ui.comboBoxSendFrom.insertItem(0, '', '') if(self.ui.comboBoxSendFrom.count() == 2): @@ -1909,7 +1912,7 @@ class MyForm(QtGui.QMainWindow): newItem = QtGui.QTableWidgetItem(unicode(toLabel, 'utf-8')) newItem.setToolTip(unicode(toLabel, 'utf-8')) newItem.setData(Qt.UserRole, str(toAddress)) - newItem.setIcon(identiconize(toAddress)) + newItem.setIcon(avatarize(toAddress, True)) self.ui.tableWidgetSent.setItem(0, 0, newItem) if fromLabel == '': newItem = QtGui.QTableWidgetItem(unicode(fromAddress, 'utf-8')) @@ -1918,7 +1921,7 @@ class MyForm(QtGui.QMainWindow): newItem = QtGui.QTableWidgetItem(unicode(fromLabel, 'utf-8')) newItem.setToolTip(unicode(fromLabel, 'utf-8')) newItem.setData(Qt.UserRole, str(fromAddress)) - newItem.setIcon(identiconize(fromAddress)) + newItem.setIcon(avatarize(fromAddress, True)) self.ui.tableWidgetSent.setItem(0, 1, newItem) newItem = QtGui.QTableWidgetItem(unicode(subject, 'utf-8)')) newItem.setToolTip(unicode(subject, 'utf-8)')) @@ -1988,7 +1991,7 @@ class MyForm(QtGui.QMainWindow): if shared.safeConfigGetBoolean(str(toAddress), 'chan'): newItem.setTextColor(QtGui.QColor(216, 119, 0)) # orange self.ui.tableWidgetInbox.insertRow(0) - newItem.setIcon(identiconize(toAddress)) + newItem.setIcon(avatarize(toAddress, True)) self.ui.tableWidgetInbox.setItem(0, 0, newItem) if fromLabel == '': @@ -2003,7 +2006,7 @@ class MyForm(QtGui.QMainWindow): self.notifierShow(unicode(_translate("MainWindow",'New Message').toUtf8(),'utf-8'), unicode(_translate("MainWindow",'From ').toUtf8(),'utf-8') + unicode(fromLabel, 'utf-8'), self.SOUND_KNOWN, unicode(fromLabel, 'utf-8')) newItem.setData(Qt.UserRole, str(fromAddress)) newItem.setFont(font) - newItem.setIcon(identiconize(fromAddress)) + newItem.setIcon(avatarize(fromAddress, True)) self.ui.tableWidgetInbox.setItem(0, 1, newItem) newItem = QtGui.QTableWidgetItem(unicode(subject, 'utf-8)')) newItem.setToolTip(unicode(subject, 'utf-8)')) @@ -2048,6 +2051,7 @@ class MyForm(QtGui.QMainWindow): self.ui.tableWidgetAddressBook.setSortingEnabled(False) self.ui.tableWidgetAddressBook.insertRow(0) newItem = QtGui.QTableWidgetItem(unicode(label, 'utf-8')) + newItem.setIcon(avatarize(address)) self.ui.tableWidgetAddressBook.setItem(0, 0, newItem) newItem = QtGui.QTableWidgetItem(address) newItem.setIcon(identiconize(address)) @@ -2078,6 +2082,7 @@ class MyForm(QtGui.QMainWindow): self.ui.tableWidgetSubscriptions.setSortingEnabled(False) self.ui.tableWidgetSubscriptions.insertRow(0) newItem = QtGui.QTableWidgetItem(unicode(label, 'utf-8')) + newItem.setIcon(avatarize(address)) self.ui.tableWidgetSubscriptions.setItem(0,0,newItem) newItem = QtGui.QTableWidgetItem(address) newItem.setIcon(identiconize(address)) @@ -2128,6 +2133,7 @@ class MyForm(QtGui.QMainWindow): newItem = QtGui.QTableWidgetItem(unicode(label, 'utf-8')) if not enabled: newItem.setTextColor(QtGui.QColor(128, 128, 128)) + newItem.setIcon(avatarize(address)) self.ui.tableWidgetBlacklist.setItem(0, 0, newItem) newItem = QtGui.QTableWidgetItem(address) newItem.setIcon(identiconize(address)) @@ -2955,6 +2961,43 @@ class MyForm(QtGui.QMainWindow): clipboard = QtGui.QApplication.clipboard() clipboard.setText(str(addressAtCurrentRow)) + def on_action_YourIdentitiesSetAvatar(self): + currentRow = self.ui.tableWidgetYourIdentities.currentRow() + addressAtCurrentRow = self.ui.tableWidgetYourIdentities.item( + currentRow, 1).text() + import hashlib + hash = hashlib.md5(addBMIfNotPresent(addressAtCurrentRow)).hexdigest() + extensions = ['PNG', 'GIF', 'JPG', 'JPEG', 'SVG', 'BMP', 'MNG', 'PBM', 'PGM', 'PPM', 'TIFF', 'XBM', 'XPM', 'TGA'] + # http://pyqt.sourceforge.net/Docs/PyQt4/qimagereader.html#supportedImageFormats + names = {'BMP':'Windows Bitmap', 'GIF':'Graphic Interchange Format', 'JPG':'Joint Photographic Experts Group', 'JPEG':'Joint Photographic Experts Group', 'MNG':'Multiple-image Network Graphics', 'PNG':'Portable Network Graphics', 'PBM':'Portable Bitmap', 'PGM':'Portable Graymap', 'PPM':'Portable Pixmap', 'TIFF':'Tagged Image File Format', 'XBM':'X11 Bitmap', 'XPM':'X11 Pixmap', 'SVG':'Scalable Vector Graphics', 'TGA':'Targa Image Format'} + filters = [] + all_images_filter = [] + for ext in extensions: + filters += [ names[ext] + ' (*.' + ext.lower() + ')' ] + all_images_filter += [ '*.' + ext.lower() ] + filters[0:0] = ['Image files (' + ' '.join(all_images_filter) + ')'] + filters[1:1] = ['All files (*.*)'] + sourcefile = QFileDialog.getOpenFileName(self, _translate("MainWindow","Set avatar..."), filter = ';;'.join(filters)) + # determine the correct filename (note that avatars don't use the suffix) + destination = shared.appdata + 'avatars/' + hash + '.' + sourcefile.split('.')[-1] + # copy the image file to the appdata folder + exists = QtCore.QFile.exists(destination) + overwrite = QtGui.QMessageBox.No + if exists: + displayMsg = _translate("MainWindow", "You have already set an avatar for this address. Do you really want to overwrite it?") + overwrite = QtGui.QMessageBox.question( + self, 'Message', displayMsg, QtGui.QMessageBox.Yes, QtGui.QMessageBox.No) + if (not exists) | (overwrite == QtGui.QMessageBox.Yes): + if overwrite == QtGui.QMessageBox.Yes: + QtCore.QFile.remove(destination) + copied = QtCore.QFile.copy(sourcefile, destination) + if not copied: + print 'couldn\'t copy :(' + return False + # set the icon + self.ui.tableWidgetYourIdentities.item( + currentRow, 0).setIcon(avatarize(addressAtCurrentRow)) + def on_context_menuYourIdentities(self, point): self.popMenu.exec_( self.ui.tableWidgetYourIdentities.mapToGlobal(point)) @@ -3101,6 +3144,7 @@ class MyForm(QtGui.QMainWindow): self.ui.tableWidgetYourIdentities.setSortingEnabled(False) self.ui.tableWidgetYourIdentities.insertRow(0) newItem = QtGui.QTableWidgetItem(unicode(label, 'utf-8')) + newItem.setIcon(avatarize(address)) self.ui.tableWidgetYourIdentities.setItem( 0, 0, newItem) newItem = QtGui.QTableWidgetItem(address) From 9a1226ba37fc8840af54997f3a57fa8b44b37135 Mon Sep 17 00:00:00 2001 From: sendiulo Date: Sat, 21 Sep 2013 09:41:23 +0200 Subject: [PATCH 10/22] - "set avatar" via context menu - remove avatar by canceling "set avatar" - get FROM address labels also from YourIdentities --- src/bitmessageqt/__init__.py | 157 +++++++++++++++++++++++++++-------- 1 file changed, 123 insertions(+), 34 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 61b93e2b..f4422ff7 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -321,6 +321,8 @@ class MyForm(QtGui.QMainWindow): "MainWindow", "Copy address to clipboard"), self.on_action_AddressBookClipboard) self.actionAddressBookSubscribe = self.ui.addressBookContextMenuToolbar.addAction(_translate( "MainWindow", "Subscribe to this address"), self.on_action_AddressBookSubscribe) + self.actionAddressBookSetAvatar = self.ui.addressBookContextMenuToolbar.addAction(_translate( + "MainWindow", "Set avatar..."), self.on_action_AddressBookSetAvatar) self.actionAddressBookNew = self.ui.addressBookContextMenuToolbar.addAction(_translate( "MainWindow", "Add New Address"), self.on_action_AddressBookNew) self.actionAddressBookDelete = self.ui.addressBookContextMenuToolbar.addAction(_translate( @@ -332,7 +334,8 @@ class MyForm(QtGui.QMainWindow): self.popMenuAddressBook = QtGui.QMenu(self) self.popMenuAddressBook.addAction(self.actionAddressBookSend) self.popMenuAddressBook.addAction(self.actionAddressBookClipboard) - self.popMenuAddressBook.addAction( self.actionAddressBookSubscribe ) + self.popMenuAddressBook.addAction(self.actionAddressBookSubscribe) + self.popMenuAddressBook.addAction(self.actionAddressBookSetAvatar) self.popMenuAddressBook.addSeparator() self.popMenuAddressBook.addAction(self.actionAddressBookNew) self.popMenuAddressBook.addAction(self.actionAddressBookDelete) @@ -350,6 +353,8 @@ class MyForm(QtGui.QMainWindow): _translate("MainWindow", "Enable"), self.on_action_SubscriptionsEnable) self.actionsubscriptionsDisable = self.ui.subscriptionsContextMenuToolbar.addAction( _translate("MainWindow", "Disable"), self.on_action_SubscriptionsDisable) + self.actionsubscriptionsSetAvatar = self.ui.subscriptionsContextMenuToolbar.addAction( + _translate("MainWindow", "Set avatar..."), self.on_action_SubscriptionsSetAvatar) self.ui.tableWidgetSubscriptions.setContextMenuPolicy( QtCore.Qt.CustomContextMenu) self.connect(self.ui.tableWidgetSubscriptions, QtCore.SIGNAL( @@ -360,6 +365,7 @@ class MyForm(QtGui.QMainWindow): self.popMenuSubscriptions.addSeparator() self.popMenuSubscriptions.addAction(self.actionsubscriptionsEnable) self.popMenuSubscriptions.addAction(self.actionsubscriptionsDisable) + self.popMenuSubscriptions.addAction(self.actionsubscriptionsSetAvatar) self.popMenuSubscriptions.addSeparator() self.popMenuSubscriptions.addAction(self.actionsubscriptionsClipboard) @@ -393,6 +399,8 @@ class MyForm(QtGui.QMainWindow): "MainWindow", "Enable"), self.on_action_BlacklistEnable) self.actionBlacklistDisable = self.ui.blacklistContextMenuToolbar.addAction(_translate( "MainWindow", "Disable"), self.on_action_BlacklistDisable) + self.actionBlacklistSetAvatar = self.ui.blacklistContextMenuToolbar.addAction(_translate( + "MainWindow", "Set avatar..."), self.on_action_BlacklistSetAvatar) self.ui.tableWidgetBlacklist.setContextMenuPolicy( QtCore.Qt.CustomContextMenu) self.connect(self.ui.tableWidgetBlacklist, QtCore.SIGNAL( @@ -405,6 +413,7 @@ class MyForm(QtGui.QMainWindow): self.popMenuBlacklist.addSeparator() self.popMenuBlacklist.addAction(self.actionBlacklistEnable) self.popMenuBlacklist.addAction(self.actionBlacklistDisable) + self.popMenuBlacklist.addAction(self.actionBlacklistSetAvatar) # Initialize the user's list of addresses on the 'Your Identities' tab. configSections = shared.config.sections() @@ -427,7 +436,7 @@ class MyForm(QtGui.QMainWindow): if not isEnabled: newItem.setTextColor(QtGui.QColor(128, 128, 128)) if shared.safeConfigGetBoolean(addressInKeysFile, 'mailinglist'): - newItem.setTextColor(QtGui.QColor(137, 04, 177)) # magenta + newItem.setTextColor(QtGui.QColor(137, 04, 177)) # magenta newItem.setIcon(identiconize(addressInKeysFile)) self.ui.tableWidgetYourIdentities.setItem(0, 1, newItem) newItem = QtGui.QTableWidgetItem(str( @@ -839,18 +848,25 @@ class MyForm(QtGui.QMainWindow): if toLabel == '': toLabel = toAddress - fromLabel = '' - t = (fromAddress,) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put( - '''select label from addressbook where address=?''') - shared.sqlSubmitQueue.put(t) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() + try: # try to get the from label fom YourIdentites (for chan messages) + fromLabel = shared.config.get(fromAddress, 'label') + checkIfChan = True + except: + fromLabel = '' + checkIfChan = False + + if fromLabel == '': # If this address wasn't in our address book... + t = (fromAddress,) + shared.sqlLock.acquire() + shared.sqlSubmitQueue.put( + '''select label from addressbook where address=?''') + shared.sqlSubmitQueue.put(t) + queryreturn = shared.sqlReturnQueue.get() + shared.sqlLock.release() - if queryreturn != []: - for row in queryreturn: - fromLabel, = row + if queryreturn != []: + for row in queryreturn: + fromLabel, = row if fromLabel == '': # If this address wasn't in our address book... t = (fromAddress,) @@ -876,7 +892,7 @@ class MyForm(QtGui.QMainWindow): newItem.setFont(font) newItem.setData(Qt.UserRole, str(toAddress)) if shared.safeConfigGetBoolean(toAddress, 'mailinglist'): - newItem.setTextColor(QtGui.QColor(137, 04, 177)) + newItem.setTextColor(QtGui.QColor(137, 04, 177)) # magenta if shared.safeConfigGetBoolean(str(toAddress), 'chan'): newItem.setTextColor(QtGui.QColor(216, 119, 0)) # orange newItem.setIcon(avatarize(toAddress, True)) @@ -894,6 +910,8 @@ class MyForm(QtGui.QMainWindow): if not read: newItem.setFont(font) newItem.setData(Qt.UserRole, str(fromAddress)) + if checkIfChan & shared.safeConfigGetBoolean(str(toAddress), 'chan'): + newItem.setTextColor(QtGui.QColor(216, 119, 0)) # orange newItem.setIcon(avatarize(fromAddress, True)) self.ui.tableWidgetInbox.setItem(0, 1, newItem) # subject @@ -1568,6 +1586,8 @@ class MyForm(QtGui.QMainWindow): fromLabel, = row self.ui.tableWidgetInbox.item( i, 1).setText(unicode(fromLabel, 'utf-8')) + self.ui.tableWidgetInbox.item( + i, 1).setIcon(avatarize(addressToLookup, True)) else: # It might be a broadcast message. We should check for that # label. @@ -1583,6 +1603,29 @@ class MyForm(QtGui.QMainWindow): fromLabel, = row self.ui.tableWidgetInbox.item( i, 1).setText(unicode(fromLabel, 'utf-8')) + self.ui.tableWidgetInbox.item( + i, 1).setIcon(avatarize(addressToLookup, True)) + else: + # It might be a chan message. We should check for that + # label. + try: + fromLabel = shared.config.get(addressToLookup, 'label') + except: + fromLabel = '' + if fromLabel == '': + fromLabel = addressToLookup + self.ui.tableWidgetInbox.item( + i, 1).setText(unicode(fromLabel, 'utf-8')) + self.ui.tableWidgetInbox.item( + i, 1).setIcon(avatarize(addressToLookup, True)) + # Set the color according to whether it is the address of a mailing + # list or not. + if shared.safeConfigGetBoolean(addressToLookup, 'chan'): + self.ui.tableWidgetInbox.item(i, 1).setTextColor(QtGui.QColor(216, 119, 0)) # orange + else: + self.ui.tableWidgetInbox.item( + i, 1).setTextColor(QApplication.palette().text().color()) + def rerenderInboxToLabels(self): for i in range(self.ui.tableWidgetInbox.rowCount()): @@ -1599,9 +1642,11 @@ class MyForm(QtGui.QMainWindow): self.ui.tableWidgetInbox.item( i, 0).setIcon(avatarize(toAddress, True)) # Set the color according to whether it is the address of a mailing - # list or not. - if shared.safeConfigGetBoolean(toAddress, 'mailinglist'): - self.ui.tableWidgetInbox.item(i, 0).setTextColor(QtGui.QColor(137, 04, 177)) + # list, a chan or neither. + if shared.safeConfigGetBoolean(toAddress, 'chan'): + self.ui.tableWidgetInbox.item(i, 0).setTextColor(QtGui.QColor(216, 119, 0)) # orange + elif shared.safeConfigGetBoolean(toAddress, 'mailinglist'): + self.ui.tableWidgetInbox.item(i, 0).setTextColor(QtGui.QColor(137, 04, 177)) # magenta else: self.ui.tableWidgetInbox.item( i, 0).setTextColor(QApplication.palette().text().color()) @@ -1987,7 +2032,7 @@ class MyForm(QtGui.QMainWindow): newItem.setFont(font) newItem.setData(Qt.UserRole, str(toAddress)) if shared.safeConfigGetBoolean(str(toAddress), 'mailinglist'): - newItem.setTextColor(QtGui.QColor(137, 04, 177)) + newItem.setTextColor(QtGui.QColor(137, 04, 177)) # magenta if shared.safeConfigGetBoolean(str(toAddress), 'chan'): newItem.setTextColor(QtGui.QColor(216, 119, 0)) # orange self.ui.tableWidgetInbox.insertRow(0) @@ -2395,7 +2440,7 @@ class MyForm(QtGui.QMainWindow): addressAtCurrentRow), 'mailinglist', 'true') shared.config.set(str(addressAtCurrentRow), 'mailinglistname', str( self.dialog.ui.lineEditMailingListName.text().toUtf8())) - self.ui.tableWidgetYourIdentities.item(currentRow, 1).setTextColor(QtGui.QColor(137, 04, 177)) + self.ui.tableWidgetYourIdentities.item(currentRow, 1).setTextColor(QtGui.QColor(137, 04, 177)) # magenta with open(shared.appdata + 'keys.dat', 'wb') as configfile: shared.config.write(configfile) self.rerenderInboxToLabels() @@ -2932,7 +2977,7 @@ class MyForm(QtGui.QMainWindow): self.ui.tableWidgetYourIdentities.item( currentRow, 2).setTextColor(QApplication.palette().text().color()) if shared.safeConfigGetBoolean(addressAtCurrentRow, 'mailinglist'): - self.ui.tableWidgetYourIdentities.item(currentRow, 1).setTextColor(QtGui.QColor(137, 04, 177)) + self.ui.tableWidgetYourIdentities.item(currentRow, 1).setTextColor(QtGui.QColor(137, 04, 177)) # magenta if shared.safeConfigGetBoolean(addressAtCurrentRow, 'chan'): self.ui.tableWidgetYourIdentities.item(currentRow, 1).setTextColor(QtGui.QColor(216, 119, 0)) # orange shared.reloadMyAddressHashes() @@ -2949,7 +2994,7 @@ class MyForm(QtGui.QMainWindow): self.ui.tableWidgetYourIdentities.item( currentRow, 2).setTextColor(QtGui.QColor(128, 128, 128)) if shared.safeConfigGetBoolean(addressAtCurrentRow, 'mailinglist'): - self.ui.tableWidgetYourIdentities.item(currentRow, 1).setTextColor(QtGui.QColor(137, 04, 177)) + self.ui.tableWidgetYourIdentities.item(currentRow, 1).setTextColor(QtGui.QColor(137, 04, 177)) # magenta with open(shared.appdata + 'keys.dat', 'wb') as configfile: shared.config.write(configfile) shared.reloadMyAddressHashes() @@ -2962,8 +3007,21 @@ class MyForm(QtGui.QMainWindow): clipboard.setText(str(addressAtCurrentRow)) def on_action_YourIdentitiesSetAvatar(self): - currentRow = self.ui.tableWidgetYourIdentities.currentRow() - addressAtCurrentRow = self.ui.tableWidgetYourIdentities.item( + self.on_action_SetAvatar(self.ui.tableWidgetYourIdentities) + + def on_action_AddressBookSetAvatar(self): + self.on_action_SetAvatar(self.ui.tableWidgetAddressBook) + + def on_action_SubscriptionsSetAvatar(self): + self.on_action_SetAvatar(self.ui.tableWidgetSubscriptions) + + def on_action_BlacklistSetAvatar(self): + self.on_action_SetAvatar(self.ui.tableWidgetBlacklist) + + def on_action_SetAvatar(self, thisTableWidget): + # thisTableWidget = self.ui.tableWidgetYourIdentities + currentRow = thisTableWidget.currentRow() + addressAtCurrentRow = thisTableWidget.item( currentRow, 1).text() import hashlib hash = hashlib.md5(addBMIfNotPresent(addressAtCurrentRow)).hexdigest() @@ -2972,31 +3030,62 @@ class MyForm(QtGui.QMainWindow): names = {'BMP':'Windows Bitmap', 'GIF':'Graphic Interchange Format', 'JPG':'Joint Photographic Experts Group', 'JPEG':'Joint Photographic Experts Group', 'MNG':'Multiple-image Network Graphics', 'PNG':'Portable Network Graphics', 'PBM':'Portable Bitmap', 'PGM':'Portable Graymap', 'PPM':'Portable Pixmap', 'TIFF':'Tagged Image File Format', 'XBM':'X11 Bitmap', 'XPM':'X11 Pixmap', 'SVG':'Scalable Vector Graphics', 'TGA':'Targa Image Format'} filters = [] all_images_filter = [] + current_files = [] for ext in extensions: filters += [ names[ext] + ' (*.' + ext.lower() + ')' ] all_images_filter += [ '*.' + ext.lower() ] + upper = shared.appdata + 'avatars/' + hash + '.' + ext.upper() + lower = shared.appdata + 'avatars/' + hash + '.' + ext.lower() + if os.path.isfile(lower): + current_files += [lower] + elif os.path.isfile(upper): + current_files += [upper] filters[0:0] = ['Image files (' + ' '.join(all_images_filter) + ')'] filters[1:1] = ['All files (*.*)'] sourcefile = QFileDialog.getOpenFileName(self, _translate("MainWindow","Set avatar..."), filter = ';;'.join(filters)) # determine the correct filename (note that avatars don't use the suffix) destination = shared.appdata + 'avatars/' + hash + '.' + sourcefile.split('.')[-1] - # copy the image file to the appdata folder exists = QtCore.QFile.exists(destination) - overwrite = QtGui.QMessageBox.No - if exists: - displayMsg = _translate("MainWindow", "You have already set an avatar for this address. Do you really want to overwrite it?") - overwrite = QtGui.QMessageBox.question( - self, 'Message', displayMsg, QtGui.QMessageBox.Yes, QtGui.QMessageBox.No) + if sourcefile == '': + # ask for removal of avatar + if exists | (len(current_files)>0): + displayMsg = _translate("MainWindow", "Do you really want to remove this avatar?") + overwrite = QtGui.QMessageBox.question( + self, 'Message', displayMsg, QtGui.QMessageBox.Yes, QtGui.QMessageBox.No) + else: + overwrite = QtGui.QMessageBox.No + else: + # ask whether to overwrite old avatar + if exists | (len(current_files)>0): + displayMsg = _translate("MainWindow", "You have already set an avatar for this address. Do you really want to overwrite it?") + overwrite = QtGui.QMessageBox.question( + self, 'Message', displayMsg, QtGui.QMessageBox.Yes, QtGui.QMessageBox.No) + else: + overwrite = QtGui.QMessageBox.No + + # copy the image file to the appdata folder if (not exists) | (overwrite == QtGui.QMessageBox.Yes): if overwrite == QtGui.QMessageBox.Yes: + for file in current_files: + QtCore.QFile.remove(file) QtCore.QFile.remove(destination) - copied = QtCore.QFile.copy(sourcefile, destination) - if not copied: - print 'couldn\'t copy :(' - return False + # copy it + if sourcefile != '': + copied = QtCore.QFile.copy(sourcefile, destination) + if not copied: + print 'couldn\'t copy :(' + return False # set the icon - self.ui.tableWidgetYourIdentities.item( + thisTableWidget.item( currentRow, 0).setIcon(avatarize(addressAtCurrentRow)) + ### + shared.reloadBroadcastSendersForWhichImWatching() + self.rerenderSubscriptions() + self.rerenderComboBoxSendFrom() + self.rerenderInboxFromLabels() + self.rerenderInboxToLabels() + self.rerenderSentFromLabels() + self.rerenderSentToLabels() def on_context_menuYourIdentities(self, point): self.popMenu.exec_( From a58164d8318d60a4073e52136d0d001e9b14505d Mon Sep 17 00:00:00 2001 From: sendiulo Date: Sat, 21 Sep 2013 13:21:36 +0200 Subject: [PATCH 11/22] - additional identicon type "empty" if you want to have a placeholder - place image named 'default.*' to display a generic user icon as fallback - place image named '[Broadcast Subscribers].*' to set the icon for broadcasts --- src/bitmessageqt/__init__.py | 92 +- src/bitmessageqt/bitmessage_icons_rc.py | 1894 ++++++++++++++--------- src/bitmessageqt/bitmessageui.py | 2 +- src/bitmessageqt/settings.py | 2 +- src/helper_startup.py | 4 + 5 files changed, 1257 insertions(+), 737 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index f4422ff7..dd1c1a7a 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -58,11 +58,6 @@ def _translate(context, text): def identiconize(address): size = 48 - # here you could put "@bitmessge.ch" or "@bm.addr" to make it compatible with other identicon generators - # instead, you could also use a pseudo-password to salt the generation of the identicons - # Attacks where someone creates an address to mimic someone else's identicon should be impossible then - # i think it should generate a random string by default - # If you include another identicon library, please generate an # example identicon with the following md5 hash: # 3fd4bf901b9d4ea1394f0fb358725b28 @@ -74,6 +69,11 @@ def identiconize(address): identicon_lib = False try: + # As an 'identiconsuffix' you could put "@bitmessge.ch" or "@bm.addr" to make it compatible with other identicon generators. (Note however, that E-Mail programs might convert the BM-address to lowercase first.) + # It can be used as a pseudo-password to salt the generation of the identicons to decrease the risk + # of attacks where someone creates an address to mimic someone else's identicon. + # If not set yet it will be filled by a random string. + # Another good idea would be to fill it with the private key of one of your addresses (because you don't need to memorize it then). identiconsuffix = shared.config.get('bitmessagesettings', 'identiconsuffix') except: identiconsuffix = '' @@ -93,7 +93,7 @@ def identiconize(address): image = qidenticon.render_identicon(int(hash, 16), size, use_two_colors, opacity, penwidth) # filename = './images/identicons/'+hash+'.png' # image.save(filename) - idcon = QIcon() + idcon = QtGui.QIcon() idcon.addPixmap(image, QtGui.QIcon.Normal, QtGui.QIcon.Off) return idcon elif identicon_lib == 'pydenticon': @@ -112,46 +112,72 @@ def identiconize(address): idcon = QtGui.QIcon() idcon.addPixmap(pix, QtGui.QIcon.Normal, QtGui.QIcon.Off) return idcon - elif identicon_lib == False: + elif identicon_lib == 'empty': + idcon = QtGui.QIcon(":/newPrefix/images/no_identicons.png") + return idcon + elif identicon_lib in ['False', 'false', 'None', 'none']: idcon = QtGui.QIcon() return idcon else: - if identicon_lib & len(identicon_lib) > 0: - print 'Error: couldn\'t find this identicon library: ', identicon_lib - print 'Control for typos!' # default to no identicons idcon = QtGui.QIcon() return idcon def avatarize(address, fallBackToIdenticon = False): - import hashlib - hash = hashlib.md5(addBMIfNotPresent(address)).hexdigest() - str_broadcast_subscribers = '[Broadcast subscribers]' - if address == str_broadcast_subscribers: - # don't hash [Broadcast subscribers] - hash = address - # print address, ' => ', hash + """ + loads a supported image for the given address' hash form 'avatars' folder + falls back to default avatar if 'default.*' file exists + falls back to identiconize(address) if second argument fallBackToIdenticon == True + """ idcon = QtGui.QIcon() - # http://pyqt.sourceforge.net/Docs/PyQt4/qimagereader.html#supportedImageFormats - # print QImageReader.supportedImageFormats () - # QImageReader.supportedImageFormats () - extensions = ['PNG', 'GIF', 'JPG', 'JPEG', 'SVG', 'BMP', 'MNG', 'PBM', 'PGM', 'PPM', 'TIFF', 'XBM', 'XPM', 'TGA'] - for ext in extensions: - upper = shared.appdata + 'avatars/' + hash + '.' + ext.upper() - lower = shared.appdata + 'avatars/' + hash + '.' + ext.lower() - if os.path.isfile(lower): - # print 'found avatar of ', address - idcon.addFile(upper) - return idcon - elif os.path.isfile(upper): - # print 'found avatar of ', address - idcon.addFile(upper) - return idcon + if shared.safeConfigGetBoolean('bitmessagesettings', 'avatars'): + import hashlib + hash = hashlib.md5(addBMIfNotPresent(address)).hexdigest() + str_broadcast_subscribers = '[Broadcast subscribers]' + if address == str_broadcast_subscribers: + # don't hash [Broadcast subscribers] + hash = address + # http://pyqt.sourceforge.net/Docs/PyQt4/qimagereader.html#supportedImageFormats + # print QImageReader.supportedImageFormats () + # QImageReader.supportedImageFormats () + extensions = ['PNG', 'GIF', 'JPG', 'JPEG', 'SVG', 'BMP', 'MNG', 'PBM', 'PGM', 'PPM', 'TIFF', 'XBM', 'XPM', 'TGA'] + # try to find a specific avatar + for ext in extensions: + lower_hash = shared.appdata + 'avatars/' + hash + '.' + ext.lower() + upper_hash = shared.appdata + 'avatars/' + hash + '.' + ext.upper() + if os.path.isfile(lower_hash): + # print 'found avatar of ', address + idcon.addFile(lower_hash) + return idcon + elif os.path.isfile(upper_hash): + # print 'found avatar of ', address + idcon.addFile(upper_hash) + return idcon + # if we haven't found any, try to find a default avatar + for ext in extensions: + lower_default = shared.appdata + 'avatars/' + 'default.' + ext.lower() + upper_default = shared.appdata + 'avatars/' + 'default.' + ext.upper() + if os.path.isfile(lower_default): + default = lower_default + idcon.addFile(lower_default) + return idcon + elif os.path.isfile(upper_default): + default = upper_default + idcon.addFile(upper_default) + return idcon + # if avatars are deactivated or none found if fallBackToIdenticon: return identiconize(address) else: + try: + identicon_lib = shared.config.get('bitmessagesettings', 'identicon') + except: + # default to no identicons + identicon_lib = False + if identicon_lib == 'empty': + idcon = QtGui.QIcon(":/newPrefix/images/no_identicons.png") + return idcon return idcon - #identiconize(address) class MyForm(QtGui.QMainWindow): diff --git a/src/bitmessageqt/bitmessage_icons_rc.py b/src/bitmessageqt/bitmessage_icons_rc.py index 377ce101..2db046a4 100644 --- a/src/bitmessageqt/bitmessage_icons_rc.py +++ b/src/bitmessageqt/bitmessage_icons_rc.py @@ -2,7 +2,7 @@ # Resource object code # -# Created: jeu. juin 13 10:47:41 2013 +# Created: Sa 21. Sep 09:44:10 2013 # by: The Resource Compiler for PyQt (Qt v4.8.4) # # WARNING! All changes made in this file will be lost! @@ -10,6 +10,495 @@ from PyQt4 import QtCore qt_resource_data = "\ +\x00\x00\x03\x66\ +\x89\ +\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ +\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\ +\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\x74\x77\x61\x72\x65\ +\x00\x41\x64\x6f\x62\x65\x20\x49\x6d\x61\x67\x65\x52\x65\x61\x64\ +\x79\x71\xc9\x65\x3c\x00\x00\x03\x08\x49\x44\x41\x54\x78\xda\x84\ +\x53\x6d\x48\x53\x51\x18\x7e\xce\xfd\xd8\x75\x9b\x8e\xdc\x2c\xdd\ +\x4c\x5d\x4e\xa7\xc9\xe6\xb7\xf6\x61\x61\x11\x14\x52\x16\xf5\xc7\ +\x0a\x0b\xa2\x3f\x41\x51\x41\x61\x7f\x0a\x84\xa2\x9f\xfd\xeb\x67\ +\x7f\xfa\x51\x44\x50\x91\x14\x15\x5a\x14\x41\x25\x6d\x44\x59\x68\ +\x69\xd9\x74\xa6\x6d\xd7\x7d\x38\xb6\xdd\x6d\x77\xa7\x73\x2d\x4d\ +\x84\xe8\x81\x87\xf7\x7d\xef\x7b\xde\xe7\xbe\xe7\x9c\xf7\x10\x30\ +\x48\x84\x20\x4f\xb3\xf8\x8b\xb3\x1b\xe9\xbc\xe5\x38\x14\xb3\x74\ +\x2f\x73\xab\x18\x47\x28\x45\x6f\x36\x0b\xff\xc2\x3a\xde\xc6\xb2\ +\x06\xcd\x61\x24\x4b\x04\xbe\x87\x09\x48\x82\x89\x8a\xb8\x62\xaf\ +\x76\x75\x5a\x4a\xcb\x9d\x31\x85\xae\x9d\x0d\xce\x15\x7c\xf1\xa3\ +\xef\x67\x18\xd0\xc8\xe1\x1f\xf0\xcf\x01\x43\x53\xc4\xf1\x33\x04\ +\x57\x20\x12\x29\xcc\x31\x5b\x84\x4d\x7b\xf6\x18\xb5\x78\xcc\x0f\ +\x07\x23\x34\x0a\xcb\xea\x0a\x19\x4f\x32\xda\x19\xc7\x53\x04\x91\ +\x99\x10\xc4\xde\xd3\xa7\x61\x30\x1a\xa1\xb2\xde\xb5\x98\xe7\xb0\ +\x85\xe5\xc7\xb4\x02\x81\x2e\xa9\x66\xfe\xb9\x86\xd6\xd6\xfd\xee\ +\xba\x3a\xcb\x3b\x8f\x47\x9e\x78\xe7\x8d\xc5\x13\x88\x4a\x3a\x1d\ +\x94\x78\x1c\x82\x28\x22\xae\x6d\x8b\x47\x23\x5b\x7e\x6d\x5e\xa0\ +\xdd\xf9\x77\xe7\xcf\x3e\xd3\x0d\xbd\xa7\x3a\xac\x2e\xa7\x15\x43\ +\x9f\x6d\xd6\xae\x43\xde\xb0\x51\x44\x74\x6c\x78\x18\xf6\x8a\x0a\ +\x68\x96\xc5\x1a\x4a\x16\x6a\x84\xad\xce\xc5\xfa\xae\xc1\x69\x53\ +\x65\xbd\xdb\x8e\x74\x32\x09\xcd\xea\xf2\x4c\xb9\x0e\x5b\x94\x0c\ +\xdc\xba\xe9\x6d\xda\xbe\xa3\xd1\xf3\xe4\xb1\x37\xf7\xb7\x40\xc1\ +\xa2\x40\x26\xbb\x28\xc0\x75\xd5\x29\x23\xc9\xb9\xb9\x8d\x99\x74\ +\x1a\x2a\xe3\xae\xfa\xf4\xc7\xf1\x92\xa2\x60\xce\xc4\x0f\x4b\x85\ +\xb3\x0a\xcf\xfb\x6e\xd2\x57\xdd\x35\x1f\x73\x43\xc9\x47\x33\x25\ +\x26\x4c\x15\xe7\x82\x27\xb5\x07\x41\x09\x87\x7c\x75\x66\xc8\x28\ +\x66\xaa\x4b\x2a\xdd\x4d\xec\x42\x85\xf0\x6c\x20\xf5\x32\x3c\xfa\ +\x4d\x3a\xd1\xe3\xd4\xd7\xb4\x54\xa5\x14\x17\xa6\xdb\xaa\x6d\x85\ +\x5b\xda\x0b\x9e\xe6\x04\x12\xe1\x3c\xc1\x8e\x2c\xfd\xc2\x7f\x6d\ +\xba\x8c\x41\x7d\x07\x1e\x99\x8e\x40\xa5\x24\xc0\x7d\xb8\xb1\x3e\ +\x96\x26\xb6\x57\xaf\x07\xfc\x74\x77\x77\x45\xc1\x6a\x87\x79\x2a\ +\x91\xc0\xd9\x8e\xa3\xb8\x3d\xe5\x41\xe9\xaa\x62\x93\xcb\x5c\x5e\ +\x6b\xa0\xba\x35\xdf\x02\x93\xe2\x92\x39\xa0\xcd\xfd\xa6\xc3\x3b\ +\x83\xf2\x2c\x69\x6c\x6e\x41\x24\x1a\x13\xef\x8f\xb4\xbe\x1f\xf7\ +\x49\x93\x49\x76\x26\xb2\x2c\x43\xb3\x1a\xd4\x54\x46\xaa\x36\x97\ +\xb9\x69\x54\x69\x23\x7c\x77\xdf\x0a\x70\xe2\x7e\x83\x24\xd4\x1c\ +\xeb\x74\xef\x5b\x19\x19\x2a\xb6\x4b\x32\xc6\x15\x0b\x82\xf9\x95\ +\xa1\xab\x0f\xfb\x3d\x49\xce\x17\x6b\x19\xf6\x0e\x0c\x6e\xf0\x6f\ +\xa3\x69\x55\x0f\x45\x35\xd0\x74\x36\x07\xa3\xd1\x27\x84\x3f\x70\ +\xe7\x4c\xe7\xfa\xf2\xee\xa6\x2a\xeb\x5a\x4b\x7e\x9e\xe4\xf3\x4d\ +\xe3\xd2\xde\x52\x9c\xbf\xeb\x43\x59\x99\x15\x72\x28\x9a\x7a\xfb\ +\xe9\xfb\x68\x5f\xff\xeb\x7b\xea\x83\x93\xd7\x97\x0d\x9e\xcc\x41\ +\x89\x36\xd7\xda\xcd\xf5\xd9\x4c\x76\xfe\x2d\x2d\x6f\x97\xaa\xd0\ +\xd5\x39\xac\x35\x90\x4c\xe5\xfc\xe6\x9e\x11\xed\x41\x2d\x61\x90\ +\xf0\xf5\x87\x2e\xc0\xda\xd0\x4e\x79\x29\x41\x05\x7d\x0c\x82\x3e\ +\xde\x36\x7d\xf5\xcd\xcb\xa2\xe3\xeb\x48\x26\x69\x20\x99\x84\x91\ +\xa8\x8a\x1e\x3f\xbc\x2f\xe8\xec\xe8\x45\x1a\x99\x04\x8d\x4c\x2c\ +\xb6\x40\xfe\x0c\x85\x05\xff\x87\xac\xfd\x71\xf9\xc7\x5f\x02\x0c\ +\x00\x00\x31\x44\x70\x94\xe4\x6d\xa8\x00\x00\x00\x00\x49\x45\x4e\ +\x44\xae\x42\x60\x82\ +\x00\x00\x02\xaf\ +\x89\ +\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ +\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\ +\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\x74\x77\x61\x72\x65\ +\x00\x41\x64\x6f\x62\x65\x20\x49\x6d\x61\x67\x65\x52\x65\x61\x64\ +\x79\x71\xc9\x65\x3c\x00\x00\x02\x51\x49\x44\x41\x54\x78\xda\x9c\ +\x53\xcf\x6b\x13\x51\x10\xfe\x36\xfb\x62\x8d\x69\x48\x62\x9b\x18\ +\x8d\xab\x5d\xd3\x84\xa2\x10\xbd\x48\x0f\x62\xa9\xd8\x93\xe0\x49\ +\x0f\xa5\x20\x52\xb0\xe0\x7f\xe0\x45\x3c\xf5\xa6\x77\x7b\xe8\x55\ +\x28\x41\x3d\x78\x28\x7a\xf0\x47\x51\xa4\xbd\x58\x0d\xa8\x60\x5a\ +\x13\x51\xd0\x43\x89\x69\xf3\x63\xb3\xc9\xee\x3e\x67\x9e\xd9\xa2\ +\x08\x4a\x1d\x18\xde\xdb\x99\xf9\xbe\xf7\xcd\x7b\xb3\xda\x8d\x85\ +\x05\xb0\x69\x9a\x76\x9e\x96\xfd\xf8\xbb\x3d\x71\x1c\x67\xad\xdb\ +\xe9\xe0\xdc\xf3\x19\x48\x0a\x08\xd7\x75\xfd\xe4\x81\xeb\x93\x93\ +\x73\x0e\x7d\x73\xc2\x95\x12\x5d\xda\x77\x3d\x4f\xed\x2b\x95\x0a\ +\x1e\x15\x8b\x57\xa5\x94\x1a\xa5\x4b\x3e\x28\x30\xf1\xf8\x32\xcc\ +\xb5\x7b\x20\x56\x4d\x72\xb1\xe3\xc0\xe9\x76\xe1\xf6\xbc\xd3\x6e\ +\xc3\x6a\x36\xd1\x68\x34\x30\x3b\x35\x35\x47\xb9\xb3\x44\x92\xf5\ +\x09\x04\xfb\xf0\xa7\x07\x57\x5a\x32\x78\x41\xd3\x2e\xe1\xc5\xea\ +\x2a\x3c\x22\x8a\xc5\x62\x68\xb5\x5a\x38\x3e\x32\xa2\x0a\xab\xd5\ +\x2a\xee\x2c\x2e\x22\x9f\x4c\xde\x5e\x29\xcc\x3e\x85\x8e\x02\x85\ +\xe7\x05\xa9\x1b\x44\x40\xcf\x65\x8f\x9e\x9c\x60\x6d\x99\x4c\x06\ +\x74\x82\x22\x89\xc7\xe3\x08\xea\xba\x22\x38\x35\x3a\x8a\x0e\xa9\ +\x0b\x85\xc3\x18\x68\x5d\x3c\x23\x1f\xbe\x7a\x2d\x3d\x77\x50\xb8\ +\x12\xc6\x5e\xe3\xd8\xf0\x26\x5d\x4c\x40\xd3\x54\xaf\xd1\x68\x54\ +\x9d\xc8\x24\x1f\x89\x8c\x09\x39\xc6\x8a\x4e\xe4\xf3\xb0\x6d\x1b\ +\x49\xc2\x54\x2b\x45\x43\xb8\x1e\x0e\xed\x8e\x26\xf7\x59\x56\x1b\ +\xbf\x2a\xe0\xd3\x7d\x25\xb2\x47\xe2\x2b\xe2\x5a\xc6\x30\x96\x14\ +\xc8\xa1\x60\x38\x16\x6a\x12\x3b\x3d\x25\xca\xe5\xf2\x36\xc0\x57\ +\xc2\x2b\x7f\xb3\x82\xc3\xa9\x14\xb8\x96\x31\x8c\x15\x8e\x87\x5c\ +\x24\x65\x26\xac\xf7\x75\x94\x0b\xd7\x30\x40\xb7\xde\x97\x1b\x47\ +\x5f\x76\xec\x37\x25\xf6\x87\x25\x04\x4b\x4b\xf8\xba\xbe\x07\x56\ +\xdb\x46\xc4\x34\x13\x8c\xe5\x16\x44\x24\x91\x4e\x4d\x27\x7e\x3e\ +\x0b\x4f\xd2\xca\xf2\x7d\x38\xc2\x50\x40\x7e\x0d\x6e\x63\x73\xf9\ +\x2e\x4e\x8f\x8d\xab\x9a\x69\x53\x2d\x29\xc6\xb2\x02\xb1\xb5\xb1\ +\x41\x7d\x59\x2a\xda\x4f\x00\x23\x9d\xc6\x97\x67\x37\x15\x41\x93\ +\x62\x3c\x58\xe6\x90\x89\x66\xbd\x8e\x46\xad\xa6\xea\x42\xa1\x10\ +\x1c\x45\xe0\x4a\xe1\xf0\xf0\x90\xb3\xd5\x88\xcc\xc8\x66\x71\xd0\ +\x3c\xf2\xc7\x1c\x7f\x2e\x6d\x0f\xa0\xaa\x67\xac\xe8\x7a\x08\x76\ +\x3a\x34\x71\xe4\xbe\xad\xbf\x7d\x87\x7f\x99\xae\x0b\x30\x56\x34\ +\x6c\xf4\x4b\xc9\x5a\x74\xec\xc4\x18\xc3\x58\xf1\xe6\x9b\xac\x6c\ +\xcd\xdf\x7a\x89\xff\xb0\xf2\x77\x54\x78\x76\x76\x91\xc7\x7a\xff\ +\xc5\x4e\x8c\x2f\xad\xf6\x43\x80\x01\x00\xc1\x52\x4e\xcc\x97\x5f\ +\x6c\x5a\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ +\x00\x00\x02\xcc\ +\x89\ +\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ +\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x01\x68\xf4\xcf\xf7\ +\x00\x00\x00\x06\x62\x4b\x47\x44\x00\xff\x00\xff\x00\xff\xa0\xbd\ +\xa7\x93\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0b\x13\x00\x00\ +\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x02\x6c\x49\x44\x41\x54\x38\ +\xcb\x4d\xd2\xbd\x6b\xdd\x65\x14\x07\xf0\xcf\xaf\xb9\x49\x9a\x10\ +\x69\x08\x06\x6a\x2c\x82\xf4\x25\x2e\xed\x50\x87\x4e\x85\x2e\x42\ +\xb2\x34\xe0\xe4\x58\x10\xea\x58\x69\x57\x47\x57\x83\xd2\x49\xc1\ +\xad\xfe\x01\xba\x28\xb8\xa8\x93\x83\xa0\x4b\x41\x89\x10\x1b\x43\ +\x20\x37\x37\xf7\xe6\xbe\xfe\xee\xef\x2d\xc7\xe1\xb9\x6a\x1f\x38\ +\x1c\x9e\xf3\x7c\xbf\xe7\x7c\x9f\x73\x4e\x16\xb7\x6e\x71\xf5\x6a\ +\x2d\xae\x5f\x2f\x82\x10\x0f\x1e\x6c\xc6\x8d\x1b\xc4\xc5\x8b\xf1\ +\x17\x21\xee\xdf\xbf\x19\x77\xee\x44\xac\xad\x45\xcc\xcf\x47\x36\ +\xc4\x11\x91\xe3\x67\x64\xb1\xb3\xc3\xa5\x4b\xbf\xd8\xdf\xff\xd1\ +\xf3\xe7\x4f\xc4\xce\x4e\xc4\x95\x2b\x11\xab\xab\x31\xa0\x16\x1b\ +\x1b\x11\x44\x45\xfc\x40\x64\x07\xc4\x18\x2f\xb0\xc7\x6e\x16\xdb\ +\xdb\xac\xaf\x7f\xab\x69\xb6\x74\x3a\x9c\x9d\x31\x1e\x27\xdf\xed\ +\x2e\xb6\x8c\xc7\x7b\x8e\x8f\xaf\x39\x3c\xe4\xf4\x94\xf3\x73\xd0\ +\x8f\xd0\xa6\x10\xcb\xcb\x4f\x83\x28\x67\x56\x10\x6d\xe2\x27\xe2\ +\x19\x91\x75\x92\x6e\x6d\x86\x7d\x56\x06\xe8\xa2\xe6\x83\xd7\xf9\ +\x22\x8b\x7b\xf7\xd8\xd8\x60\x71\xf1\x13\x79\xfe\xc8\xd9\xd9\x6f\ +\xda\xed\xf7\xb4\xdb\x7f\xea\xf5\xb4\x5c\xbe\xbc\x60\x7e\xbe\xd0\ +\xef\xd3\xe9\x30\x1a\xbd\x6d\x30\xd8\x33\x99\x7c\xa7\x28\xb6\x5b\ +\xca\x72\xa2\xdb\xe5\xe0\x20\x89\xac\x6b\xea\x5a\x33\x1c\x6e\x9d\ +\xb1\xd9\x72\x7c\x3c\xa7\xdd\xe6\xf0\x90\xe9\x14\x54\x11\x4e\xd0\ +\xe1\xab\x96\xa3\x23\xfa\xfd\xf4\x18\x21\x90\xe3\x24\x89\x7f\x23\ +\x8b\x56\x2b\x9a\xba\x56\x63\x0e\x25\xfe\xc6\xef\x18\xf0\x59\xd6\ +\xe6\xd3\x21\x8f\x4a\x34\x29\xe8\x45\xfa\xb6\x55\xb2\xd6\x84\x0f\ +\x8f\xd9\xef\x26\xa0\x5e\x02\x8d\x96\x79\xe5\x35\x64\x71\xf7\x2e\ +\x6b\x6b\xac\xac\xb0\xb0\xf0\x58\x96\x7d\xac\xae\x97\x14\x45\xd2\ +\x35\x9d\x52\x14\xe4\x39\x93\x49\x6e\x32\xf9\xc8\x64\xb2\x2b\xcf\ +\x29\xcb\xd9\x42\x2c\x2d\x7d\xee\xc2\x85\x87\xaa\x2a\x01\x87\x43\ +\x46\xa3\x44\x2e\x4b\x9a\x26\x59\x59\xa6\x58\x9e\x8b\xe9\x74\xb7\ +\xe2\x49\x4b\x51\x3c\x55\x96\x0f\x4d\xa7\x89\xd8\xeb\xa5\x4d\xc8\ +\x73\xaa\x8a\x08\x20\xcb\xa8\x6b\x65\x84\x1c\x13\x1e\x17\xcc\x65\ +\x71\xfb\x76\xa1\xae\x17\xe4\x79\x5a\xa3\xe1\x30\x91\x9b\xe6\x7f\ +\x32\xff\xb5\x77\x34\x6b\xd4\x20\x25\x39\x69\x39\x3a\x3a\x50\x55\ +\xd7\x54\x55\xaa\x58\x96\xa2\x69\xbc\x7c\xce\x67\xed\x1f\xa6\xe1\ +\xe9\xe2\x0c\x05\x07\x59\xc3\xcd\x29\xbf\x56\xcc\xd5\xb3\x4a\x90\ +\xcd\x7c\x83\xe9\x4b\xe4\x53\xf4\x53\xc2\x66\x81\xb7\xb2\x6e\x92\ +\xb5\x30\xe6\xeb\x9c\xad\x7f\xe7\xd9\xa0\x4a\x55\xe4\x33\xc9\xa3\ +\xd9\x1d\xdf\x2c\xf3\xee\xab\x34\x59\x0f\xe3\x19\xa0\x9f\xfc\x9b\ +\x23\xde\x1f\xf1\x4e\xce\x66\x91\x12\xfd\xd1\xf0\xfd\x1c\x5f\x2e\ +\xb1\x7f\x09\xeb\x33\xfb\x07\x6a\x4f\x76\xe7\x35\x05\x41\x4b\x00\ +\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ +\x00\x00\x02\x24\ +\x89\ +\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ +\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\ +\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\x74\x77\x61\x72\x65\ +\x00\x41\x64\x6f\x62\x65\x20\x49\x6d\x61\x67\x65\x52\x65\x61\x64\ +\x79\x71\xc9\x65\x3c\x00\x00\x01\xc6\x49\x44\x41\x54\x78\xda\x8c\ +\x53\x3d\x4b\x03\x41\x10\x7d\xd9\x3b\xf5\x44\x45\x8b\x44\x34\xa2\ +\xc4\x42\x49\x2b\xe8\xcf\xb0\xf3\x07\x28\xd6\x82\x9d\x20\x58\x88\ +\x0a\x36\x82\x8d\x95\xa0\x58\x0b\x16\xda\x8b\xd8\x08\x09\x44\x0b\ +\x8b\x54\x22\x7e\x44\x10\x41\x42\xce\xe4\x2e\xb7\x1f\xce\x9c\xe6\ +\xcb\x5c\xc0\xe5\x06\x6e\xdf\xbe\xf7\x66\x76\x76\x37\x96\xdd\x05\ +\x62\xc0\x12\x80\x14\xfe\x3f\x1e\x0d\x70\x3c\xbb\x66\x60\x1b\xfa\ +\xa3\x6f\x72\x6e\xf5\x62\x03\x5a\x03\x4a\x75\x96\x59\x16\x20\x04\ +\xb2\xfb\xf3\x5b\x35\x48\x84\x06\x06\xe2\x25\x93\x01\x1b\x18\x29\ +\x61\xaa\x7e\x7b\x10\xce\xeb\xcc\x63\x3e\xeb\x42\x03\xc5\x49\x35\ +\x44\x6f\x3c\x8e\xfb\xcb\x4b\xca\x22\x60\x44\x7b\x30\xce\xeb\xcc\ +\x63\x3e\xeb\x78\xd8\xfa\xc7\xc9\x1a\x1a\x4e\xa0\xe2\x96\x70\x73\ +\x7e\x51\xaf\xd8\xf3\x3c\x38\x8e\x53\x9f\x4f\x4c\x4f\x81\x79\xa4\ +\xb1\x6a\x98\xfd\xeb\x24\x0c\xed\x7d\x38\x39\x1a\x46\x08\x74\x75\ +\xe3\x29\x9f\xc7\x44\x3a\x0d\x1d\x54\xeb\x26\xcc\xe3\x0a\xfe\x1a\ +\x58\x5a\x05\x50\x32\x68\x34\x4c\xc4\x30\xd0\xd7\x87\x28\x9c\x34\ +\x56\xbb\x81\x54\xd0\xdc\xa8\xdf\x11\x13\x16\x1d\x08\x63\x11\x78\ +\x94\x81\x51\x92\xb2\x35\x88\x42\x59\x90\x94\x39\x0a\xef\x50\x41\ +\x00\xdd\x54\xaa\x1f\x28\x2c\xf6\x6c\xa2\xfa\xa6\xa8\x99\x92\x22\ +\x80\xef\x2b\x64\xa6\x8f\x5a\x0d\xa4\xaa\x19\x48\xda\x6b\x23\x53\ +\xd9\xf5\x70\x32\x53\x6e\xba\x45\x22\x0c\xf7\xae\x04\xd2\x44\x54\ +\x10\x96\xda\xa8\xc0\xfd\x2c\xc2\xae\x54\x90\xcb\xe5\x90\x48\x24\ +\xc2\x7e\xa4\x52\x29\xe8\x62\xa9\x53\x0f\xa8\x59\x4d\xd7\xd8\x25\ +\x62\x77\xb9\x8c\x34\x1d\x63\xbd\x2a\x9a\xeb\xd2\x57\xab\xc1\xdd\ +\x23\x90\x4e\xc2\x79\x79\x7a\xa5\x9b\xaa\x9a\x7a\xe0\xe3\xe3\x74\ +\xa5\xed\x39\x0c\xc6\x87\xe0\x55\xe1\xe4\x0b\xc0\x02\x1b\xec\x9c\ +\x61\xf0\x60\x19\xfd\xe3\xe3\xc9\xd6\xf3\x1e\x1b\x89\x7e\x4f\x76\ +\x17\x6e\xaf\xd1\xcf\xba\x6d\xa0\x68\xb3\xe9\xfd\x33\x0a\x87\x7b\ +\xeb\x57\xff\x7d\xcb\x0f\xef\x28\xb0\x8e\xa2\xf8\x2d\xc0\x00\x14\ +\x2c\x1a\x71\xde\xeb\xd2\x54\x00\x00\x00\x00\x49\x45\x4e\x44\xae\ +\x42\x60\x82\ +\x00\x00\x06\xe3\ +\x89\ +\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ +\x00\x00\x90\x00\x00\x00\x90\x08\x06\x00\x00\x00\xe7\x46\xe2\xb8\ +\x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\ +\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0e\xc4\x00\x00\x0e\xc4\ +\x01\x95\x2b\x0e\x1b\x00\x00\x06\x85\x49\x44\x41\x54\x78\x9c\xed\ +\x9d\x4b\x72\xe3\x38\x10\x44\xc1\x8e\x5e\xf8\x3c\x3a\x8e\x0f\xa8\ +\xe3\xf8\x3c\xda\x79\x16\x0e\x4c\xd3\x10\x89\x6f\x7d\xb2\x0a\xf5\ +\x96\x0e\x59\x2c\x24\x12\x29\x90\x20\xc1\xe3\xf3\xe3\xeb\x3b\xa5\ +\x94\x9e\xaf\xc7\x91\x0c\x92\xeb\xb7\x8c\x65\xed\x8f\xb2\x03\x2c\ +\x37\x46\xbb\x86\x59\xac\x69\x7e\xd6\xfa\x28\xff\x90\xb1\xd6\xa8\ +\x8c\x45\x23\x59\xd1\xfa\x4a\xdb\x5b\x03\x65\xac\x34\xae\xc4\x92\ +\x91\xd0\x35\xbe\xd3\xf2\xf9\x7a\x1c\x47\xeb\x43\xe7\x0f\x53\x17\ +\x26\x81\x05\x23\xa1\x6a\xdb\xe3\x89\x6e\x03\x9d\xff\x69\xb5\x30\ +\x0d\x90\x8d\x84\xa6\x69\x8f\x56\xb9\xe6\x5f\x85\x8f\x88\x8c\xd6\ +\xe8\x5e\x10\x8d\x84\xa2\xe5\x4c\xff\x4f\x1b\xa8\xfc\x22\x6b\x20\ +\x19\x49\x5b\xc3\x51\x2d\xce\xf5\xbe\x15\x3e\x2b\xac\xb6\x08\xb3\ +\x20\x18\x49\x4b\x3b\x8a\xbe\x26\x33\xd0\xd5\x97\x5b\x42\xd3\x48\ +\xd2\x9a\xad\xb4\xb5\xac\xf5\xb2\x70\x0a\x31\xc3\x48\xfd\x48\x69\ +\xc5\xd1\xaf\x6c\x06\xba\x3b\xa0\x15\x24\x8d\xc4\xad\x11\x55\x5b\ +\xae\xea\xbc\x2d\x9c\x5a\x40\x8b\x46\x92\x32\x11\x97\x36\x12\x7d\ +\xf8\x97\xf2\x00\x35\x2c\x2d\xda\x5e\xad\x0f\x22\x4c\xb6\x7b\xe1\ +\xa8\xf5\xae\xdf\xaa\x9d\xc9\x29\x1a\xa2\x91\x6a\x97\xec\x5b\x9f\ +\x59\x81\x4a\x0b\x8d\xfe\x12\x4b\xa0\x12\xa4\x44\x9a\xb9\x80\x86\ +\x94\x48\xdc\xb5\xd4\xfa\xa8\xd9\x79\xd6\xe7\x01\x35\x28\x96\x6f\ +\x34\xcf\x58\x11\xfa\x46\x2d\x81\x4a\x24\x13\x89\xe3\x2c\x53\x32\ +\x91\x90\xce\x10\xbb\x3a\xcb\xcb\xb5\x11\x89\xab\xec\x9c\xcb\x41\ +\x88\xfd\x00\x93\x40\x25\x94\x89\xa4\x31\x62\x29\x8f\xa9\x35\xdf\ +\xea\xd1\x9e\x75\x64\x51\x32\x63\x24\xce\x0b\x68\x94\x35\xdc\x7d\ +\xbf\x05\xcd\x61\x13\xa8\x64\x24\x91\xb4\x85\x3f\x33\x93\x48\x08\ +\xf5\xf7\x0e\x9a\xa1\x91\x85\xd0\xb0\xcc\x55\x03\xb9\xea\xa3\x9c\ +\x8f\xd5\xee\x3f\x47\xd7\xf7\x0a\xb3\x06\xca\x48\x5c\x25\x46\x9a\ +\xd0\x4b\x30\xd2\xde\x3f\x5c\x5f\x2c\x05\x72\x47\xd4\x40\xd4\x72\ +\x86\x21\x03\xa1\x62\xad\x33\x3e\x3f\xbe\xbe\x51\x8d\x3f\xaa\xe5\ +\xb0\x81\x50\x3b\xeb\xf9\x7a\x1c\xa8\xb5\x65\x90\x8d\x33\x8b\x99\ +\xb3\xb0\x5e\x10\x27\xa4\x48\xb5\xd4\x98\x19\x80\x53\x3f\x61\xe8\ +\x23\x3d\x25\x8c\x44\xf2\x98\x38\x25\xee\x12\xa8\xc4\xfb\x5a\x15\ +\x15\xb3\x83\x6d\x7a\x12\xad\x3d\xba\x47\x91\x48\xa4\x1d\x12\xa7\ +\xc4\x7d\x02\x95\x78\x5a\xab\xa2\x62\x65\x60\x2d\x9d\xc6\x5b\x4b\ +\xa1\x33\x14\x89\xb4\x63\xe2\x94\x6c\x97\x40\x25\x56\xd7\xaa\xa8\ +\x58\x1d\x44\xcb\x17\x12\x2d\xa7\xd0\x99\x9e\x44\x8a\xc4\x79\x07\ +\xfe\x66\xee\x1e\x76\x5b\xab\xa2\x82\x42\x37\x92\xa5\x0c\x2f\x29\ +\x74\xc6\x63\x9b\x38\xd8\x7e\x0e\x74\x45\xa4\x4f\x3f\x64\x8b\xa9\ +\x1e\x46\x6c\xcc\x71\xc6\x89\x04\x4a\x7b\x24\xce\x19\xca\xc1\x4e\ +\x7a\x3b\x87\xb5\x14\x8a\xc4\x59\x67\xcb\x04\xda\xd9\x34\xd4\x83\ +\x9c\xfc\x86\x32\xe4\x14\x8a\xc4\xa1\x67\x8b\x04\x0a\xd3\xfc\xc0\ +\x31\xb8\x59\x6e\x69\x45\x49\xa1\x48\x1c\x7e\x5c\x26\x50\x98\xe6\ +\x1d\xae\x41\xcd\x76\x53\xbd\xd6\x6e\x1b\x61\x1e\x59\x4c\xec\xcd\ +\x17\xac\xc1\x39\x98\xff\x46\x27\x07\x2b\xb8\x9c\x03\x59\xc3\xca\ +\x26\x9b\x57\xdf\xcf\xfe\x60\x21\xca\x19\x19\x32\x12\x1d\xcd\xf5\ +\xdd\xac\x06\x0a\xf3\xe8\x21\x65\x4a\x91\x47\x9b\xc3\x48\x6d\xac\ +\xa6\x90\xab\xd3\xf8\xe0\x07\x49\x33\x8a\x6d\xae\x10\x86\x6a\x63\ +\x31\x85\x5c\x2f\x65\xec\x88\xb4\x09\x45\xb7\x77\x09\x63\xb5\xb1\ +\x96\x42\x5b\xdd\xce\xe1\x1d\x0d\xf3\x89\x6f\x30\x15\x06\x6b\x63\ +\x29\x85\xb6\xbe\xa5\xd5\x13\x5a\xa6\x53\xd9\xe2\x2e\x8c\xd6\xc6\ +\x4a\x0a\xc5\x63\x3d\x0e\xd0\x34\x9b\xda\x26\x9b\x61\xb8\x36\x16\ +\x52\x28\x1e\x6d\x36\x8e\xb6\xc9\x54\xb7\xf9\x0d\xe3\xb5\xd1\x36\ +\x48\x8b\xd8\xde\xc5\x30\x08\xe6\x52\xdf\x68\x3c\x0c\xd8\x06\xc1\ +\x28\x77\x6c\xbb\xc5\x9d\x75\x50\x4c\xa5\x9e\x40\x29\x85\x11\x7b\ +\x40\x31\x4c\xc9\x36\xdb\xfc\x7a\x02\xc9\x4c\x10\x09\x94\x52\x18\ +\xb2\x07\x24\xe3\x64\xa6\xde\xb5\x65\xf5\x29\x82\x80\x1e\xf6\x97\ +\xb5\x05\xbe\x89\xe7\xc2\x0c\x82\xf4\x0b\x00\xf7\xbe\xb0\x98\x0b\ +\xb5\x41\xfa\xd5\x80\x7a\xe5\x65\x98\x47\x0f\xd1\xd3\xf8\x30\x92\ +\x3e\x28\x29\xd4\x6d\xa0\x30\x8d\x5f\x54\x96\x32\xc2\x50\xfa\x20\ +\xa4\x50\x97\x81\xc2\x2c\x7e\x51\xbd\x9d\x23\x8c\xa5\x8f\x76\x0a\ +\x35\x0d\x14\x26\xf1\x0b\xc4\x2d\xad\x61\x30\x7d\x34\x53\xa8\x6a\ +\xa0\x30\x87\x5f\xa0\x1e\xeb\x09\xa3\xe9\xa3\x95\x42\xb7\x06\x0a\ +\x53\xf8\x05\xf2\xd1\xe6\x30\x9c\x3e\x1a\x29\x74\x69\xa0\x30\x83\ +\x5f\xa0\xb7\x77\x09\xe3\xe9\x23\x9d\x42\x6f\x06\x0a\x13\xf8\xc5\ +\xc4\x16\x77\x61\x40\x7d\x24\x53\xe8\x97\x81\xa2\xf3\xfd\x62\x6a\ +\x9b\xdf\x30\xa2\x3e\x52\x29\xf4\xbf\x81\xa2\xd3\xfd\x62\xf2\x55\ +\x07\x61\x48\x7d\x24\x52\xe8\x4f\x4a\xd1\xd9\x9e\xe1\x36\xd1\xf1\ +\xf9\xf1\xf5\xcd\xd9\xc1\xda\xf7\xab\x04\xbc\xc4\x1b\x0b\x15\x79\ +\xbe\x1e\x22\x0f\x76\x72\x06\x04\xcc\xb3\xf1\x3b\xf1\x7c\x3d\x0e\ +\xc9\x9f\x75\x4e\x93\xb2\x3d\x99\x1a\xe9\xf3\x8e\xc7\xb9\x60\x24\ +\x90\x00\xd2\x89\x73\x05\xd7\x80\x66\x49\xa0\x48\x9f\x1f\xb4\x4d\ +\x23\x41\x24\x10\x03\x08\x89\x73\x05\xc7\xc0\x26\x4f\xa0\x9d\xd3\ +\x07\xd1\x34\xdc\x44\x02\x11\x80\x9a\x38\x57\x50\x0f\x70\xd2\x04\ +\xda\x2d\x7d\xac\x98\x86\x93\x48\xa0\x09\x2c\x25\xce\x15\x94\x03\ +\x9d\x2c\x81\x76\x48\x1f\xcb\xa6\xe1\x22\x12\xa8\x13\x6f\xe6\x81\ +\x7a\xb0\xd0\x6b\xfa\x9c\x4d\xf3\xf9\xf1\xf5\xed\xb5\x9d\x2b\x44\ +\x02\x5d\x50\x9b\xe3\x78\x32\x12\x45\x3b\x96\xe7\x40\x5e\xc4\x4c\ +\x69\xec\x67\x2a\xb7\xdb\xdb\x4f\xdb\x28\x91\x40\x69\xed\xac\xca\ +\x7a\x22\xad\xd6\xbe\x94\x40\x96\x85\x4b\x89\x36\x3d\x76\x4d\xa4\ +\x2d\x13\x88\xf3\x3a\x8e\xc5\x44\x5a\xa9\x77\x3a\x81\xac\x89\x94\ +\x92\x6c\x3a\xec\x92\x48\x5b\x24\x90\xe6\x95\x63\x2b\x89\x34\x5b\ +\xe3\x54\x02\x59\x10\x24\x25\xac\xd1\xef\x35\x91\x5c\x26\x10\xf2\ +\x5a\x15\x72\x22\xcd\xd4\x35\x9c\x40\xa8\x8d\x4f\xc9\xd6\xe8\x46\ +\xd6\x71\x04\x37\x09\x64\xc9\x3c\xc8\x8c\x1a\x7b\xc8\x40\x68\xa3\ +\xc6\xfa\x5a\x55\xfe\xa9\xb5\x6c\xfe\xa1\xc2\x51\x3a\xa8\x34\x4e\ +\xeb\x33\x2b\x70\xb4\xb9\x56\x1b\xa2\xc6\x35\xba\xe7\x40\x08\x0d\ +\xb3\xbe\x56\xd5\x53\x4b\xfe\x0c\x82\xde\x3d\x0c\x77\x88\x06\x14\ +\x23\x76\x65\xad\x6b\xe6\xff\x28\x8e\x4d\x75\xfc\x59\x7a\xea\xee\ +\x4a\x20\xad\x46\x58\x5f\xab\xa2\x38\x16\x7a\x22\x75\x35\x50\xba\ +\xf8\x99\x9f\x2a\xae\x63\x20\xbd\x16\x3d\x25\xbc\xbe\x68\x26\x90\ +\x64\xc1\xd6\xd7\xaa\x24\xea\x47\x4b\xa4\x66\x83\xd1\xb7\x1f\xa1\ +\xaa\xaf\x76\x07\xe2\xec\xff\x4a\xa0\xdd\x3f\xd5\x04\xe2\x2e\x0e\ +\xe9\x0c\x69\x26\x91\x10\xea\xd7\x4e\xa4\xaa\x00\x5c\x45\x71\x4c\ +\x8e\xa9\xa9\x75\x0c\x82\x71\xee\x90\xee\x33\xd1\x0b\x5a\x1c\xc2\ +\x7b\x9d\xa3\xad\x42\xad\x8b\xaa\x81\x3c\x9c\x95\x58\x32\xcf\x19\ +\xee\x7e\x9c\x9e\x38\xce\x1e\x90\x1a\xb4\xd3\x5a\x54\xb8\x2e\x88\ +\xb2\x18\xc8\xcb\xfe\x7f\x35\x76\x35\x52\xd9\xee\x37\x11\x56\x0e\ +\xa0\x21\xaa\xf6\xf5\x90\xdd\x8c\xc4\x62\x20\xef\xd7\x41\x7a\xd8\ +\xc9\x48\xe7\xb6\xfe\x6a\xf4\xe8\x97\x21\x88\x86\x62\xa0\x0c\x82\ +\x26\x33\x8c\xe8\xb8\x6c\x20\x24\x91\xd0\x0c\x94\x41\xd2\x68\x84\ +\x51\x0f\x34\x6f\xcc\xba\xfa\x27\x24\x50\x0d\x94\x41\xd4\xac\x87\ +\x96\xae\x43\x06\x42\x16\x01\xdd\x40\x19\x64\x0d\x6b\xb4\x7c\x51\ +\x5d\x47\xb1\xd0\x68\x2b\x06\xca\x58\xd0\xf4\x8a\xbb\x25\x9d\x4b\ +\x03\x59\x6a\xa4\x35\x03\x65\x2c\x69\x7c\xa6\xd4\xfb\xd7\xdb\x62\ +\x2c\x36\xca\xaa\x81\x32\x16\x35\x4f\xe9\x9f\xee\xff\x01\x8b\x65\ +\xc9\x17\x1c\x9e\xef\x70\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\ +\x60\x82\ +\x00\x00\x02\xf0\ +\x89\ +\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ +\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\ +\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\x74\x77\x61\x72\x65\ +\x00\x41\x64\x6f\x62\x65\x20\x49\x6d\x61\x67\x65\x52\x65\x61\x64\ +\x79\x71\xc9\x65\x3c\x00\x00\x02\x92\x49\x44\x41\x54\x78\xda\x84\ +\x53\x5f\x48\x53\x61\x14\x3f\xf7\xee\x6e\x22\x0b\x4c\x6b\xac\x60\ +\x6a\xb5\x39\xdd\x06\x36\xd9\x70\x6b\xb8\x31\xd4\xb2\x35\x82\x3d\ +\x4c\x50\x50\x08\x1f\x24\x84\x24\x56\xea\xc3\xdc\x2a\x63\x36\x1d\ +\x4d\xa1\xb0\xf0\xc1\x17\x1f\x7c\xb0\x3f\x0f\x51\x96\x12\xe8\xa6\ +\x84\xa6\xeb\x45\xa9\x84\x22\x25\x08\xb7\x97\xec\x45\xe6\xee\xdd\ +\xed\x7c\x97\xb5\x36\x1a\x74\xe0\xc7\xe1\xfb\x9d\xf3\xfb\x9d\xf3\ +\xdd\x8f\x4b\x41\x4e\xd0\xc5\xc5\x20\xa9\xa8\x80\xc3\xdd\x5d\x48\ +\x1f\x1c\x40\x75\x43\x03\x68\x6d\x36\xa0\x45\x22\xa0\x69\x5a\x85\ +\x2d\x8d\x90\x1f\x3f\x18\x28\x10\x4a\xa3\x11\xaa\x4d\x26\x41\x98\ +\x89\xaa\x74\x3a\xdd\x38\x3b\x34\xf4\xf8\x0f\x41\x21\xdc\x7e\xff\ +\xd5\x3c\x83\x53\x7a\xbd\x20\x16\x31\x79\x74\x55\x9a\xe3\x9a\x66\ +\x03\x81\x47\xd1\xf5\x75\xf8\xb0\xb5\x05\x75\x3a\x1d\x58\xb1\x0f\ +\x79\x4a\xe8\x2c\xaf\xab\x83\xd3\x48\x30\xcc\x3f\x0b\x55\x71\x1c\ +\xd7\xfc\x34\x18\x9c\xc0\x0c\x89\xfd\x7d\x28\x2d\x2b\xa3\x30\xf3\ +\xa4\xc8\x11\x03\x53\x47\x07\x88\xc4\xe2\x42\x37\x51\xe3\x84\xf3\ +\xcf\x42\xa1\x87\xc9\x64\x12\x44\x78\x1d\x8d\x52\x09\xdf\xe3\x71\ +\xbe\x5c\x2e\x17\x1a\xb0\x4e\xd3\x50\x38\xd4\x2c\xc7\x5d\x78\x82\ +\xe2\x58\x2c\x06\x57\x70\xc8\xd6\xe6\x26\x9c\x51\x28\xc0\x6e\x30\ +\x80\xba\xb2\x12\x2e\x79\x3c\xd7\x70\x83\x85\x42\x06\xd5\x1c\xcb\ +\xb6\x3c\x0f\x87\x1f\xbc\x5f\x5b\x83\xbb\x7e\x3f\x1c\xe0\x8b\xdc\ +\x1a\x1c\x24\x2b\x0b\x1f\xd6\xd1\xdb\xdb\x8b\xd3\x17\xf0\x1e\xdb\ +\x4c\x01\xf1\xc5\x17\x13\x13\xe3\xef\x56\x56\xe0\x8e\xd7\x9b\x2d\ +\x04\x46\x47\x41\x52\x54\x04\x2d\x3d\x3d\xd7\x29\x8a\x9a\x47\xa3\ +\xcf\x84\xcf\x35\xa8\x61\x59\xd6\xf1\x6a\x72\x32\xbc\xbc\xb4\x04\ +\xbe\xfe\xfe\x6c\x61\x64\x6c\x0c\x8c\xf5\xf5\xd0\xdc\xdd\xed\x41\ +\xf1\x1b\x51\x46\x9c\x6b\xa0\x21\xe2\xd7\x53\x53\xf7\x23\x8b\x8b\ +\xe0\xef\xeb\xcb\x8a\xef\x85\xc3\x60\xb6\x58\xa0\xb1\xab\xeb\x06\ +\x8a\xe7\x50\xfc\x29\x77\x65\x62\xa0\xe1\x52\x29\xe7\xfc\xf4\x74\ +\x28\x8a\xe2\x00\xae\x2d\x91\x48\x84\xe2\xed\x60\x10\x2c\x56\x2b\ +\xd8\x3b\x3b\xfb\x80\x88\x19\x26\x2b\xfe\x8a\xdf\xe7\xcb\xea\x2a\ +\x30\x38\xf9\xf2\xdb\x99\x99\x91\xbd\x78\x1c\xc6\x87\x87\x41\x2a\ +\x95\x0a\x0d\x37\x7d\x3e\x41\x6c\x6b\x6f\x1f\xc0\xc9\x2f\x71\xf2\ +\x47\xc2\xef\xe0\xab\xec\x6c\x6c\xfc\x5d\x41\xdf\xda\xaa\x3d\xeb\ +\x76\x0f\xfc\xe4\x79\x7e\x2e\x12\xe1\x5d\x2e\x17\x3f\x1f\x8d\xf2\ +\xbf\xf0\x4c\x78\x52\x37\xb4\xb5\x81\xa2\xb6\xb6\xf0\x83\x9f\xd0\ +\x6a\xa1\xc6\xe9\xd4\xa9\x1d\x0e\x2f\x31\xd9\xde\xdb\xe3\xf7\x31\ +\x93\x33\xe1\x49\xfd\x7f\x41\xfe\x98\x92\x12\x95\xaa\x49\x6e\x36\ +\x0f\x11\x13\x92\x8f\xaa\x54\x76\xe4\x8f\x21\x4a\x49\x1d\x71\x04\ +\x51\x8c\x28\x42\x88\x33\x3a\x8a\xca\x1c\x4e\x22\x8e\x4b\x64\x32\ +\x85\x58\x26\x3b\x97\x4a\x24\x96\x0f\x13\x89\x6f\xc8\xa5\x10\x6c\ +\x26\x13\x1c\xe6\x70\x04\xdc\x6f\x01\x06\x00\x2d\x06\x04\x62\x7f\ +\xe8\x51\x71\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ +\x00\x00\x03\x37\ +\x89\ +\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ +\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\ +\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\x74\x77\x61\x72\x65\ +\x00\x41\x64\x6f\x62\x65\x20\x49\x6d\x61\x67\x65\x52\x65\x61\x64\ +\x79\x71\xc9\x65\x3c\x00\x00\x02\xd9\x49\x44\x41\x54\x78\xda\x6c\ +\x93\x5d\x48\x14\x51\x14\xc7\xff\xbb\xb3\xbb\xb6\xab\x95\x5b\x46\ +\xae\x99\xba\x7e\x40\x42\x46\x12\xa5\x4b\x49\xe8\x83\x60\x22\x11\ +\x3d\x94\xf6\xd6\x43\xe9\x6b\x81\x6f\x46\xd1\x43\x54\xd0\xf6\x49\ +\xf6\x24\x44\x81\x6f\x3d\x04\x8b\x08\x9a\x60\x59\xb6\x18\x65\xe9\ +\x22\xbb\xce\xb8\xea\xfa\xb1\x8b\xdf\xe3\x3a\xbb\xb3\xce\xed\xcc\ +\x75\x35\xad\x2e\x73\xb9\x77\xe6\x9c\xf3\xbf\xbf\x73\xee\x19\xc3\ +\xad\xf6\x76\x18\x0c\x06\x3e\x35\x4d\x83\x3e\x68\x5f\x47\x8b\x03\ +\xff\x1f\xdd\xeb\x89\x44\x40\x4d\x24\xc0\x18\x83\x69\xbb\xc5\x48\ +\x22\x09\x32\xd0\xc8\x6a\xa9\xaf\x6f\x9d\x8e\x44\x61\xb7\x5b\xb8\ +\xb0\x46\xce\x43\x81\x00\x3a\x07\x07\x1b\xf5\x33\x68\xfa\x79\xcc\ +\x0e\x6d\x12\x30\x0a\x02\x74\xf5\xc8\x9c\x02\x8a\x44\x24\xb2\x86\ +\x99\xd9\x28\xa6\x66\x56\x21\xcb\x32\xee\x36\x34\xb4\x92\xbd\x8a\ +\xbc\x8b\xf4\x90\x4d\x82\x3a\xc2\x71\x24\xf1\x21\x08\x42\xc5\xe4\ +\x62\x08\xcb\xb2\x8a\xe2\x9c\x83\xc8\xb0\x5a\xa1\xae\xaf\xe3\xc7\ +\xf0\x3c\xde\x7a\x3c\x28\xc9\xc8\x68\x7d\xd2\xde\x7e\x83\xdc\xdd\ +\x26\x8d\x0c\x9b\xc8\x23\x81\x15\xe4\xe6\x59\x77\x20\x0f\x07\xa7\ +\x91\x99\xbe\x1f\xa9\x29\x36\x9c\x38\xea\x42\x82\x6c\x66\xb3\x19\ +\xe5\xc1\xa0\xc2\x09\xd4\x8d\x9c\xe1\x17\x65\x3d\x03\x04\xc7\xd6\ +\x78\x71\xf4\x7a\xea\xc8\x35\xe5\xe5\xf8\xe8\xf3\xc1\xbe\xc7\x8c\ +\x0c\xbb\x8d\x93\x08\x24\x10\x8b\xc5\x0c\x1b\x02\xaa\xca\xc9\x8b\ +\x9c\xa9\xf0\x4b\xab\x70\x1e\xb6\xf0\x53\x74\xc7\x21\x71\x03\x59\ +\x1f\x83\xbf\xfc\xa8\xad\xa8\x24\x1b\xa3\xca\xa9\x88\x93\xc0\xc9\ +\xee\x6e\x12\x88\xc7\xb9\x80\x38\x1e\x85\xd1\x68\xc0\xd8\x64\x9c\ +\x13\xd0\x83\x92\xc2\xd3\x9c\x44\x7f\x5f\x54\xc7\x71\x60\x5f\x0a\ +\xdf\xc7\x07\x06\xd0\xe8\x76\x5f\xd3\xc2\xe1\x21\x23\xa1\x70\x9c\ +\xc2\x1c\x1b\x4f\xa1\x20\x67\x17\xf2\xf9\x4c\x41\x2e\xd1\x64\x67\ +\x0b\xc8\xcb\xb7\x52\x41\xe3\x98\x5f\x4a\x60\xc4\x1f\x42\xaf\xf7\ +\x3b\xca\x9a\x9a\x8e\x45\x80\x3b\x26\x42\xe1\x04\x52\x68\x8d\xdf\ +\xc0\x58\x28\xc6\x4f\xd7\x34\xb6\x45\xc2\x98\x02\x9b\x05\xb0\xa8\ +\xfd\x08\x8e\x2e\xa3\xe6\xfa\x55\xd4\xb9\x5c\x3d\x17\x19\xbb\x67\ +\x8a\x25\x05\x0a\xb2\x6d\x18\x9d\x8c\x22\x2f\xcb\xca\x6f\x80\x17\ +\x32\xb9\x1a\xa8\x37\xc4\x2e\x2f\x7c\xa1\xf7\xa8\x39\x75\x1c\xee\ +\xa7\x12\x66\x9d\xce\xaf\xdf\x04\xa1\xd3\xa4\x28\xca\x06\xc1\x54\ +\x92\x60\x4a\xd9\xca\x7b\x93\x24\xb6\xf8\x09\xc6\xb9\x37\x28\xab\ +\x3c\x8b\x8e\x8e\x7e\x5c\xba\xd0\x82\xd7\x7d\x3d\xe1\xb6\x89\x09\ +\xfc\x21\x38\x44\x04\xa1\x28\xf2\x75\x02\x60\x8b\x60\x61\xb6\x17\ +\xe2\xec\x73\x54\x53\xf0\xc3\x47\xee\xd1\x8e\x61\xc7\x87\xa1\x97\ +\xcd\x7e\x4d\x96\x97\xe5\x70\x38\xdf\x34\x23\x8a\xd8\xeb\x70\x18\ +\x44\x22\xd0\x5b\x5c\x9a\x56\x92\x79\x33\x44\x17\x46\x11\x09\x3c\ +\xc0\x19\x57\x29\x1e\x3f\x7b\x21\x05\x25\xa5\xb9\xcf\x23\x7d\x01\ +\xa4\xcd\xe6\xd7\x83\x90\x7e\xe4\xfc\xf9\x9b\x14\xc0\x88\x40\x5f\ +\x18\x9d\xcc\xd6\x89\xfd\xb3\xe7\x36\x63\xf2\x08\x7b\xd7\x56\xc5\ +\x6a\xaf\x94\xbe\x22\x5f\xfb\xdf\xbf\xa6\xde\x4d\xb9\xbb\x8b\x8b\ +\xab\x8d\x69\x69\xff\x18\x97\xbc\xde\xfb\x97\xcf\xa5\xfe\x1c\x5e\ +\xcd\xec\x93\xc2\x96\x81\x15\x9f\xaf\x8b\x3e\x8b\xdb\x7d\x7e\x0b\ +\x30\x00\x66\x8d\xa1\xfd\x87\x65\xe6\x27\x00\x00\x00\x00\x49\x45\ +\x4e\x44\xae\x42\x60\x82\ +\x00\x00\x02\xa1\ +\x89\ +\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ +\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\ +\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\x74\x77\x61\x72\x65\ +\x00\x41\x64\x6f\x62\x65\x20\x49\x6d\x61\x67\x65\x52\x65\x61\x64\ +\x79\x71\xc9\x65\x3c\x00\x00\x02\x43\x49\x44\x41\x54\x78\xda\xa4\ +\x93\xcf\x6b\x13\x51\x10\xc7\xbf\xfb\x23\x6b\xd3\x4d\x63\x53\x51\ +\x69\x93\x2d\x52\x62\xc5\xb6\x54\x08\x52\x28\x28\xb4\xa8\x08\xc5\ +\x5e\x84\xfe\x01\x3d\x49\x2f\x9e\x7a\x29\x96\x9e\xf2\x07\xf4\xa4\ +\x88\x17\x7f\x1c\x82\x22\x2a\xb9\x05\x5a\xe8\x49\x94\x0a\xb1\x58\ +\x24\x97\x68\x49\xa5\x31\x35\x90\xd2\xcd\x26\xd9\xcd\x6e\xd6\x99\ +\xad\x5b\x82\xb4\x50\x71\x60\xf6\xbd\x37\xcc\x7c\xdf\x7c\xde\xdb\ +\x27\xb8\xae\x8b\xff\x31\x79\x66\x69\xe9\x70\x21\x08\xc2\x34\x0d\ +\x7d\xff\x50\xbf\x23\xf3\xd7\x69\xb5\xfc\x40\xf4\x4d\x32\xf9\xe8\ +\x24\x3d\x09\xe4\x77\x17\x17\xe7\x64\xda\x15\x92\x28\xc2\x34\x4d\ +\x8e\x8b\x0e\x21\x7d\x2e\xba\xa8\x14\xbe\x42\x8b\x0f\x63\x20\xd2\ +\x3a\x52\x40\xa4\x1a\xbb\xd9\x14\xbd\x0e\x04\x5a\x28\x8a\x82\x9a\ +\x61\x88\x36\x09\xec\x15\xbe\xa0\x98\xdf\x84\x08\x07\x5a\xe2\x32\ +\x0a\xa5\x12\x82\xc1\x20\x6c\xdb\x46\x6f\x4f\x8f\x27\x20\xd1\xc6\ +\xbe\xc0\x34\x5c\xb7\x8f\x15\x03\x8a\x72\x6d\x65\x7d\x1d\xdb\xbb\ +\x3a\x8a\xe5\x32\x6a\xe1\x5f\xa8\x7e\x32\xd0\xa0\x42\xdf\xce\x77\ +\x77\xe3\x4a\x3c\x8e\x00\xe5\x37\x2d\x4b\x94\x6d\xc7\x39\x86\xfb\ +\xe6\x91\xdc\x4f\x33\x19\x9c\x56\x55\x5c\xd0\x34\x58\x96\x25\xc9\ +\xdc\x06\x73\x3f\xcb\xba\xf8\xfe\xfe\x35\xc6\x6f\xcf\xe0\xd6\xc0\ +\xf1\xdc\x6a\x67\x27\x62\xd1\x28\x6c\x3a\x78\xcb\x34\x45\x91\x05\ +\x98\xfb\xe7\x87\x57\xd8\x5c\x4d\x61\x63\xe5\x25\x9a\x8e\x83\xb5\ +\x6c\x16\x1b\x5b\x5b\xf8\x98\xcb\x79\x6b\x76\xce\x4b\x2e\x2f\xa7\ +\x9f\xa4\x52\xab\xcd\x03\x01\x49\x66\x0e\x56\x3b\xa3\x0d\xa1\x5a\ +\xad\xe2\x5c\xff\x10\x2c\x62\x8e\xc5\x62\xde\xae\x2a\xb5\x6b\xfd\ +\x39\x03\xe6\x56\x43\x21\x69\x6e\x76\xf6\x06\xd5\xc1\xd0\xf5\x80\ +\xcc\x1c\xac\xf6\xee\x6d\x1a\x86\x61\x60\x2d\x93\xc6\x9d\xeb\xf7\ +\x91\xa3\x9d\x7d\x2b\x45\x22\xa8\xd7\xeb\x18\x4f\x24\x50\xd3\xf5\ +\xca\xd9\x78\x7c\x21\x14\x0e\x77\x39\x86\x51\x96\x99\x83\x3b\x78\ +\xf1\x70\x9e\x52\xe7\xbd\x82\x7a\xad\x86\xab\xa3\xa3\xde\x3c\x48\ +\xcc\xbe\x71\x9e\x24\x49\xdf\xec\x7c\xfe\xf9\x1e\xc0\xe7\x5e\x11\ +\x99\x83\x3b\x60\xae\xde\x91\x91\x05\x1e\x2d\xe2\xf5\xbd\x3d\xce\ +\x79\xa4\x60\x5c\x9c\x9c\xdc\xa1\xe2\x22\x79\x03\x97\xa6\xa6\x1e\ +\xec\x9a\xa6\x5b\xa1\x57\xc5\x73\x1e\x7f\xe8\xfa\xa1\xb7\xc7\x39\ +\x8f\xe7\xe4\x88\x8d\x8d\x1d\x5c\x6d\xd7\xe0\xe0\x3d\x49\x55\xfb\ +\xab\xfb\xfb\xba\xd2\x68\x6c\x5b\x1d\x1d\x1a\xf3\xf9\x6d\xff\x1d\ +\x27\xee\x02\xf9\xe3\xf6\x7f\xe3\x14\x79\x84\xaf\xf9\x04\x6f\xc8\ +\xe3\xf6\x5a\x27\x1b\x9e\x98\xc0\x6f\x01\x06\x00\x48\xae\x45\x78\ +\x60\x4e\x1f\xe2\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ +\ +\x00\x00\x02\x75\ +\x89\ +\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ +\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\ +\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\x74\x77\x61\x72\x65\ +\x00\x41\x64\x6f\x62\x65\x20\x49\x6d\x61\x67\x65\x52\x65\x61\x64\ +\x79\x71\xc9\x65\x3c\x00\x00\x02\x17\x49\x44\x41\x54\x78\xda\xa4\ +\x93\x4f\x6b\x13\x51\x14\xc5\xcf\x9b\x37\x93\x49\xc6\xa9\x25\xa3\ +\x15\x63\x16\x2e\x02\x06\xad\x0b\x45\xc1\x95\x5f\xc0\x3f\x0b\x21\ +\x1b\xa5\x20\xb8\x31\xea\x7c\x82\xac\xf4\x2b\x54\x5b\x23\x88\x1b\ +\x4b\xb3\x70\xe1\x42\xb7\x82\x20\xe8\x42\xbb\x70\xa1\x62\x36\xa2\ +\xa0\x51\xda\x1a\xc2\x4c\xa6\x26\x93\xcc\x78\xef\x75\x5a\xb4\x2a\ +\x54\x7c\x70\xb8\x2f\x93\x7b\x7e\xf7\xbc\x37\x89\xaa\xd5\x6a\x50\ +\x4a\x9d\x06\xb0\x07\xff\xb6\x3e\xa5\x69\xfa\xc0\x4c\x92\x84\x3f\ +\x94\x9b\xcd\xe6\xcd\x38\x8e\xb7\xe4\xb4\x2c\x0b\xf5\x7a\xfd\x12\ +\xef\xcd\xd1\x68\xc4\xd5\x18\x0c\x06\xf0\x7d\x1f\x0c\x64\x11\x5d\ +\xea\x78\x3c\x46\x18\xf6\xa9\xa6\x62\xf4\x3c\x0f\xf3\xf3\xd7\x41\ +\x3e\xe3\x67\x80\xe2\xca\x86\x6a\xb5\x0a\x4e\xf2\xed\x68\x05\xa3\ +\xc7\x2f\xb1\xb2\xf2\x95\x9e\x6b\x32\xdb\xb0\xed\x3c\xa2\x28\x60\ +\x33\x4b\x09\x20\x8b\x6d\xf0\x43\x9e\xc6\x49\x58\x69\x79\x07\x56\ +\x57\xbb\x64\x88\x91\xcf\x6f\x13\xb3\x65\xe5\xa9\x27\x16\x00\xf9\ +\x8c\x0d\x23\x49\x33\x48\x00\x34\x39\x8a\x22\xa8\xbd\xbb\x08\x94\ +\x60\xf2\x60\x05\xe5\xd9\x3a\x26\x4f\x1c\x13\x90\x69\xda\x92\x90\ +\x3d\xec\x35\x86\xc3\x21\x48\x3f\x40\xa5\x22\x9d\x37\x84\x73\xed\ +\xbc\x5c\xd6\xf6\xe9\x0a\x3c\xff\x14\x7a\x8d\x16\x01\x8b\xa8\x35\ +\xaf\x12\xc0\x94\x04\xec\x61\xef\xc6\x11\xb8\x26\xef\xbf\xa0\xdf\ +\x5d\x43\xf7\xe1\x53\xb8\x07\xf6\xa1\x78\xf9\x24\xfa\xb7\x1f\xc1\ +\x75\x8b\x48\x5b\x4b\xb8\x77\xf7\x19\xbf\x72\x49\xb0\x7e\x04\x93\ +\x29\xb4\x24\x8e\xe3\x38\xe8\xf5\x7a\x30\x0c\x0b\xfa\xed\x07\x84\ +\xfe\x2c\x0a\x85\x09\x0c\x0c\x2d\x46\x5e\xb6\xad\xd7\x13\x68\x01\ +\xb4\xdb\x6d\x94\x4a\xa5\x1c\x37\x34\x1a\x8d\x2d\xfd\x0e\xb8\x37\ +\x08\x82\x5c\xa7\xd3\x01\x63\x3d\xd7\x75\x67\xb4\xd6\xbb\x37\x37\ +\xd2\xa4\xb2\x4c\x31\xcd\x8f\x9b\xbf\xa3\x0b\xff\x4c\xf7\xb5\xc0\ +\x80\x02\x69\x82\xfb\x7e\xe9\x98\xce\x01\xaf\x86\x7e\xb6\xbf\x41\ +\xfb\xdf\xf8\xa4\x40\xfd\x35\xe7\xe2\xd4\x2d\xbc\x89\x8f\xc8\x7e\ +\xbf\xb5\x84\x73\xcb\x17\xff\xd4\xc6\x53\x77\x92\x2a\xa4\x43\xa4\ +\xc3\xa4\xaa\x24\x5a\x0c\x71\xe6\xce\x59\x01\xdc\xbf\xd0\xe2\xf2\ +\x82\x93\x93\xde\x65\xfb\xe7\xa4\xd7\x9c\xc0\xca\x8e\xe1\x66\x72\ +\xf8\xad\xe0\x8a\x33\x83\x29\x75\x5c\xc6\x2c\xa7\x4f\x30\x17\x2d\ +\x64\x43\xd7\x38\x7a\xa6\x48\xf1\x9f\xe6\x7f\xd6\x77\x01\x06\x00\ +\xf9\x1f\x11\xa0\x42\x25\x9c\x34\x00\x00\x00\x00\x49\x45\x4e\x44\ +\xae\x42\x60\x82\ \x00\x00\x07\x62\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ @@ -131,294 +620,6 @@ qt_resource_data = "\ \x22\xb5\x33\x33\xbc\x6e\xdb\x3c\x9d\x48\x10\xfc\x1f\x86\x93\xb9\ \x1a\xfd\x43\x9a\xa3\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\ \x82\ -\x00\x00\x02\x24\ -\x89\ -\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ -\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\ -\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\x74\x77\x61\x72\x65\ -\x00\x41\x64\x6f\x62\x65\x20\x49\x6d\x61\x67\x65\x52\x65\x61\x64\ -\x79\x71\xc9\x65\x3c\x00\x00\x01\xc6\x49\x44\x41\x54\x78\xda\x8c\ -\x53\x3d\x4b\x03\x41\x10\x7d\xd9\x3b\xf5\x44\x45\x8b\x44\x34\xa2\ -\xc4\x42\x49\x2b\xe8\xcf\xb0\xf3\x07\x28\xd6\x82\x9d\x20\x58\x88\ -\x0a\x36\x82\x8d\x95\xa0\x58\x0b\x16\xda\x8b\xd8\x08\x09\x44\x0b\ -\x8b\x54\x22\x7e\x44\x10\x41\x42\xce\xe4\x2e\xb7\x1f\xce\x9c\xe6\ -\xcb\x5c\xc0\xe5\x06\x6e\xdf\xbe\xf7\x66\x76\x76\x37\x96\xdd\x05\ -\x62\xc0\x12\x80\x14\xfe\x3f\x1e\x0d\x70\x3c\xbb\x66\x60\x1b\xfa\ -\xa3\x6f\x72\x6e\xf5\x62\x03\x5a\x03\x4a\x75\x96\x59\x16\x20\x04\ -\xb2\xfb\xf3\x5b\x35\x48\x84\x06\x06\xe2\x25\x93\x01\x1b\x18\x29\ -\x61\xaa\x7e\x7b\x10\xce\xeb\xcc\x63\x3e\xeb\x42\x03\xc5\x49\x35\ -\x44\x6f\x3c\x8e\xfb\xcb\x4b\xca\x22\x60\x44\x7b\x30\xce\xeb\xcc\ -\x63\x3e\xeb\x78\xd8\xfa\xc7\xc9\x1a\x1a\x4e\xa0\xe2\x96\x70\x73\ -\x7e\x51\xaf\xd8\xf3\x3c\x38\x8e\x53\x9f\x4f\x4c\x4f\x81\x79\xa4\ -\xb1\x6a\x98\xfd\xeb\x24\x0c\xed\x7d\x38\x39\x1a\x46\x08\x74\x75\ -\xe3\x29\x9f\xc7\x44\x3a\x0d\x1d\x54\xeb\x26\xcc\xe3\x0a\xfe\x1a\ -\x58\x5a\x05\x50\x32\x68\x34\x4c\xc4\x30\xd0\xd7\x87\x28\x9c\x34\ -\x56\xbb\x81\x54\xd0\xdc\xa8\xdf\x11\x13\x16\x1d\x08\x63\x11\x78\ -\x94\x81\x51\x92\xb2\x35\x88\x42\x59\x90\x94\x39\x0a\xef\x50\x41\ -\x00\xdd\x54\xaa\x1f\x28\x2c\xf6\x6c\xa2\xfa\xa6\xa8\x99\x92\x22\ -\x80\xef\x2b\x64\xa6\x8f\x5a\x0d\xa4\xaa\x19\x48\xda\x6b\x23\x53\ -\xd9\xf5\x70\x32\x53\x6e\xba\x45\x22\x0c\xf7\xae\x04\xd2\x44\x54\ -\x10\x96\xda\xa8\xc0\xfd\x2c\xc2\xae\x54\x90\xcb\xe5\x90\x48\x24\ -\xc2\x7e\xa4\x52\x29\xe8\x62\xa9\x53\x0f\xa8\x59\x4d\xd7\xd8\x25\ -\x62\x77\xb9\x8c\x34\x1d\x63\xbd\x2a\x9a\xeb\xd2\x57\xab\xc1\xdd\ -\x23\x90\x4e\xc2\x79\x79\x7a\xa5\x9b\xaa\x9a\x7a\xe0\xe3\xe3\x74\ -\xa5\xed\x39\x0c\xc6\x87\xe0\x55\xe1\xe4\x0b\xc0\x02\x1b\xec\x9c\ -\x61\xf0\x60\x19\xfd\xe3\xe3\xc9\xd6\xf3\x1e\x1b\x89\x7e\x4f\x76\ -\x17\x6e\xaf\xd1\xcf\xba\x6d\xa0\x68\xb3\xe9\xfd\x33\x0a\x87\x7b\ -\xeb\x57\xff\x7d\xcb\x0f\xef\x28\xb0\x8e\xa2\xf8\x2d\xc0\x00\x14\ -\x2c\x1a\x71\xde\xeb\xd2\x54\x00\x00\x00\x00\x49\x45\x4e\x44\xae\ -\x42\x60\x82\ -\x00\x00\x02\x7a\ -\x89\ -\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ -\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\ -\x00\x00\x00\x06\x62\x4b\x47\x44\x00\xff\x00\xff\x00\xff\xa0\xbd\ -\xa7\x93\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0b\x13\x00\x00\ -\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07\x74\x49\x4d\x45\x07\ -\xdd\x06\x0d\x08\x1d\x33\x51\xf1\xd4\x9e\x00\x00\x02\x07\x49\x44\ -\x41\x54\x38\xcb\x65\x91\x3d\x6b\x14\x41\x18\xc7\x7f\x73\xb7\x2c\ -\x6c\x0c\xb9\x20\xa6\x0d\x58\x45\x90\x88\x1f\x40\xb1\x12\x2e\x8d\ -\x82\x5f\x41\x08\x58\x05\xb4\xb5\x0c\xd8\x09\xa2\x20\x08\x7e\x88\ -\x58\x05\xac\x04\xfb\x08\x56\x42\x40\x48\x9d\xdc\xe5\xf6\x66\xe7\ -\x75\x67\xc7\xe2\x19\x72\x07\x0e\xfc\x18\x06\xe6\xff\x32\xcf\xa8\ -\x9c\x1f\x00\xb7\x81\x09\x70\x0b\xa8\xf7\x40\x1d\x42\x7a\x02\xe1\ -\x21\x78\xc0\xfe\x82\xee\x07\x74\x9f\x41\x9f\x83\x41\xf0\xa8\x9c\ -\x1f\x17\x83\x4d\xa0\x7e\x0d\xea\x18\xfa\x46\x84\xae\xe0\x01\x0b\ -\x18\x0b\xe6\x2d\x98\xf7\x72\x0e\xa8\x9c\x0f\x80\x49\x0d\xf5\x09\ -\xa8\x29\xf4\xe5\x72\x57\x76\x0f\x44\x20\xac\x19\x9a\x53\x70\xcf\ -\x21\x84\x11\xd4\x00\x1f\xa1\x9f\x4a\xad\x05\x70\x05\x5c\x96\x7d\ -\x06\x5c\x03\xcb\x62\xda\x01\x66\x9a\xb3\x79\x17\x42\x8f\xca\xf9\ -\xd9\x3e\x54\x67\x90\xc6\x92\xb8\x28\xe8\x92\x9e\x80\x5c\x48\x80\ -\x23\xa5\x88\x31\xa4\x10\xb8\x5f\x41\x38\x84\x38\x96\x6a\x4b\x60\ -\x5e\x12\x6d\xa9\x9e\x91\xa5\x80\x9e\x10\x32\xd6\x82\x31\x8c\xbd\ -\xe7\x55\x05\x66\x2a\xce\xb6\x18\x2c\xcb\x84\x03\x30\xb0\xbe\x62\ -\x14\x71\xd7\x09\xd6\xf2\xa8\x02\xbd\xfb\xff\xe0\x62\x11\xe7\x1b\ -\x71\xce\x10\x23\x78\x0f\xc6\xc0\x72\x09\xc6\xb0\x5b\x49\x62\xcf\ -\xea\xdb\xe2\xda\xbb\x57\xe2\x94\xa0\xef\xb9\x69\x50\x0c\x18\xc1\ -\xf2\x02\xda\x32\x34\x49\xcf\x39\x93\x33\x37\x0c\x83\xa4\x5b\x0b\ -\x5a\x43\xdb\x0a\x5d\xc7\xc5\x08\xda\x53\x99\x7a\x4b\x4a\x96\x18\ -\x13\x21\x48\x5a\x4a\xab\xda\x5a\xc3\xf5\x35\xcc\x66\x42\xdb\x82\ -\xb5\xfc\x54\x29\xb1\xef\x1c\x67\x31\x32\xee\x7b\x49\x04\x50\x4a\ -\xf6\x94\xc0\x39\xa9\x7c\x79\x09\x57\x57\xb0\x58\x40\x08\xa4\xba\ -\xe6\x5e\x65\x0c\xbf\xad\xe5\x93\x73\x1c\xc5\x28\xc9\xc3\xb0\x12\ -\xf7\xbd\xbc\xb5\x6d\x61\x3e\x17\xb1\xf7\x30\x1a\xf1\xa1\x69\x38\ -\x57\xb3\x19\x68\x4d\xdd\x75\x9c\x58\xcb\x34\x04\x11\xae\xd7\xb7\ -\x56\x0c\xb4\x96\x33\xf0\x6d\x63\x83\x17\x77\xee\x90\xaa\x61\x80\ -\x61\x20\xc4\xc8\x81\x73\x1c\x19\xc3\xb1\x73\x6c\x7a\x0f\x21\x48\ -\x7d\x6b\x85\x18\xd1\x4a\xf1\xa6\x69\xf8\xb2\xb5\x05\xdb\xdb\xa0\ -\xe6\x73\xf9\x96\xb6\x95\x7a\x6d\xcb\x5d\xad\x79\xa9\x35\x4f\xad\ -\x65\xcf\x7b\x88\x91\x3f\x29\xf1\x7d\x3c\xe6\x6b\xd3\xf0\x77\x32\ -\x81\x9d\x1d\xe1\x1f\x3c\x20\x6c\x94\x65\x65\x77\x27\x00\x00\x00\ -\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ -\x00\x00\x03\x37\ -\x89\ -\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ -\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\ -\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\x74\x77\x61\x72\x65\ -\x00\x41\x64\x6f\x62\x65\x20\x49\x6d\x61\x67\x65\x52\x65\x61\x64\ -\x79\x71\xc9\x65\x3c\x00\x00\x02\xd9\x49\x44\x41\x54\x78\xda\x6c\ -\x93\x5d\x48\x14\x51\x14\xc7\xff\xbb\xb3\xbb\xb6\xab\x95\x5b\x46\ -\xae\x99\xba\x7e\x40\x42\x46\x12\xa5\x4b\x49\xe8\x83\x60\x22\x11\ -\x3d\x94\xf6\xd6\x43\xe9\x6b\x81\x6f\x46\xd1\x43\x54\xd0\xf6\x49\ -\xf6\x24\x44\x81\x6f\x3d\x04\x8b\x08\x9a\x60\x59\xb6\x18\x65\xe9\ -\x22\xbb\xce\xb8\xea\xfa\xb1\x8b\xdf\xe3\x3a\xbb\xb3\xce\xed\xcc\ -\x75\x35\xad\x2e\x73\xb9\x77\xe6\x9c\xf3\xbf\xbf\x73\xee\x19\xc3\ -\xad\xf6\x76\x18\x0c\x06\x3e\x35\x4d\x83\x3e\x68\x5f\x47\x8b\x03\ -\xff\x1f\xdd\xeb\x89\x44\x40\x4d\x24\xc0\x18\x83\x69\xbb\xc5\x48\ -\x22\x09\x32\xd0\xc8\x6a\xa9\xaf\x6f\x9d\x8e\x44\x61\xb7\x5b\xb8\ -\xb0\x46\xce\x43\x81\x00\x3a\x07\x07\x1b\xf5\x33\x68\xfa\x79\xcc\ -\x0e\x6d\x12\x30\x0a\x02\x74\xf5\xc8\x9c\x02\x8a\x44\x24\xb2\x86\ -\x99\xd9\x28\xa6\x66\x56\x21\xcb\x32\xee\x36\x34\xb4\x92\xbd\x8a\ -\xbc\x8b\xf4\x90\x4d\x82\x3a\xc2\x71\x24\xf1\x21\x08\x42\xc5\xe4\ -\x62\x08\xcb\xb2\x8a\xe2\x9c\x83\xc8\xb0\x5a\xa1\xae\xaf\xe3\xc7\ -\xf0\x3c\xde\x7a\x3c\x28\xc9\xc8\x68\x7d\xd2\xde\x7e\x83\xdc\xdd\ -\x26\x8d\x0c\x9b\xc8\x23\x81\x15\xe4\xe6\x59\x77\x20\x0f\x07\xa7\ -\x91\x99\xbe\x1f\xa9\x29\x36\x9c\x38\xea\x42\x82\x6c\x66\xb3\x19\ -\xe5\xc1\xa0\xc2\x09\xd4\x8d\x9c\xe1\x17\x65\x3d\x03\x04\xc7\xd6\ -\x78\x71\xf4\x7a\xea\xc8\x35\xe5\xe5\xf8\xe8\xf3\xc1\xbe\xc7\x8c\ -\x0c\xbb\x8d\x93\x08\x24\x10\x8b\xc5\x0c\x1b\x02\xaa\xca\xc9\x8b\ -\x9c\xa9\xf0\x4b\xab\x70\x1e\xb6\xf0\x53\x74\xc7\x21\x71\x03\x59\ -\x1f\x83\xbf\xfc\xa8\xad\xa8\x24\x1b\xa3\xca\xa9\x88\x93\xc0\xc9\ -\xee\x6e\x12\x88\xc7\xb9\x80\x38\x1e\x85\xd1\x68\xc0\xd8\x64\x9c\ -\x13\xd0\x83\x92\xc2\xd3\x9c\x44\x7f\x5f\x54\xc7\x71\x60\x5f\x0a\ -\xdf\xc7\x07\x06\xd0\xe8\x76\x5f\xd3\xc2\xe1\x21\x23\xa1\x70\x9c\ -\xc2\x1c\x1b\x4f\xa1\x20\x67\x17\xf2\xf9\x4c\x41\x2e\xd1\x64\x67\ -\x0b\xc8\xcb\xb7\x52\x41\xe3\x98\x5f\x4a\x60\xc4\x1f\x42\xaf\xf7\ -\x3b\xca\x9a\x9a\x8e\x45\x80\x3b\x26\x42\xe1\x04\x52\x68\x8d\xdf\ -\xc0\x58\x28\xc6\x4f\xd7\x34\xb6\x45\xc2\x98\x02\x9b\x05\xb0\xa8\ -\xfd\x08\x8e\x2e\xa3\xe6\xfa\x55\xd4\xb9\x5c\x3d\x17\x19\xbb\x67\ -\x8a\x25\x05\x0a\xb2\x6d\x18\x9d\x8c\x22\x2f\xcb\xca\x6f\x80\x17\ -\x32\xb9\x1a\xa8\x37\xc4\x2e\x2f\x7c\xa1\xf7\xa8\x39\x75\x1c\xee\ -\xa7\x12\x66\x9d\xce\xaf\xdf\x04\xa1\xd3\xa4\x28\xca\x06\xc1\x54\ -\x92\x60\x4a\xd9\xca\x7b\x93\x24\xb6\xf8\x09\xc6\xb9\x37\x28\xab\ -\x3c\x8b\x8e\x8e\x7e\x5c\xba\xd0\x82\xd7\x7d\x3d\xe1\xb6\x89\x09\ -\xfc\x21\x38\x44\x04\xa1\x28\xf2\x75\x02\x60\x8b\x60\x61\xb6\x17\ -\xe2\xec\x73\x54\x53\xf0\xc3\x47\xee\xd1\x8e\x61\xc7\x87\xa1\x97\ -\xcd\x7e\x4d\x96\x97\xe5\x70\x38\xdf\x34\x23\x8a\xd8\xeb\x70\x18\ -\x44\x22\xd0\x5b\x5c\x9a\x56\x92\x79\x33\x44\x17\x46\x11\x09\x3c\ -\xc0\x19\x57\x29\x1e\x3f\x7b\x21\x05\x25\xa5\xb9\xcf\x23\x7d\x01\ -\xa4\xcd\xe6\xd7\x83\x90\x7e\xe4\xfc\xf9\x9b\x14\xc0\x88\x40\x5f\ -\x18\x9d\xcc\xd6\x89\xfd\xb3\xe7\x36\x63\xf2\x08\x7b\xd7\x56\xc5\ -\x6a\xaf\x94\xbe\x22\x5f\xfb\xdf\xbf\xa6\xde\x4d\xb9\xbb\x8b\x8b\ -\xab\x8d\x69\x69\xff\x18\x97\xbc\xde\xfb\x97\xcf\xa5\xfe\x1c\x5e\ -\xcd\xec\x93\xc2\x96\x81\x15\x9f\xaf\x8b\x3e\x8b\xdb\x7d\x7e\x0b\ -\x30\x00\x66\x8d\xa1\xfd\x87\x65\xe6\x27\x00\x00\x00\x00\x49\x45\ -\x4e\x44\xae\x42\x60\x82\ -\x00\x00\x02\xa1\ -\x89\ -\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ -\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\ -\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\x74\x77\x61\x72\x65\ -\x00\x41\x64\x6f\x62\x65\x20\x49\x6d\x61\x67\x65\x52\x65\x61\x64\ -\x79\x71\xc9\x65\x3c\x00\x00\x02\x43\x49\x44\x41\x54\x78\xda\xa4\ -\x93\xcf\x6b\x13\x51\x10\xc7\xbf\xfb\x23\x6b\xd3\x4d\x63\x53\x51\ -\x69\x93\x2d\x52\x62\xc5\xb6\x54\x08\x52\x28\x28\xb4\xa8\x08\xc5\ -\x5e\x84\xfe\x01\x3d\x49\x2f\x9e\x7a\x29\x96\x9e\xf2\x07\xf4\xa4\ -\x88\x17\x7f\x1c\x82\x22\x2a\xb9\x05\x5a\xe8\x49\x94\x0a\xb1\x58\ -\x24\x97\x68\x49\xa5\x31\x35\x90\xd2\xcd\x26\xd9\xcd\x6e\xd6\x99\ -\xad\x5b\x82\xb4\x50\x71\x60\xf6\xbd\x37\xcc\x7c\xdf\x7c\xde\xdb\ -\x27\xb8\xae\x8b\xff\x31\x79\x66\x69\xe9\x70\x21\x08\xc2\x34\x0d\ -\x7d\xff\x50\xbf\x23\xf3\xd7\x69\xb5\xfc\x40\xf4\x4d\x32\xf9\xe8\ -\x24\x3d\x09\xe4\x77\x17\x17\xe7\x64\xda\x15\x92\x28\xc2\x34\x4d\ -\x8e\x8b\x0e\x21\x7d\x2e\xba\xa8\x14\xbe\x42\x8b\x0f\x63\x20\xd2\ -\x3a\x52\x40\xa4\x1a\xbb\xd9\x14\xbd\x0e\x04\x5a\x28\x8a\x82\x9a\ -\x61\x88\x36\x09\xec\x15\xbe\xa0\x98\xdf\x84\x08\x07\x5a\xe2\x32\ -\x0a\xa5\x12\x82\xc1\x20\x6c\xdb\x46\x6f\x4f\x8f\x27\x20\xd1\xc6\ -\xbe\xc0\x34\x5c\xb7\x8f\x15\x03\x8a\x72\x6d\x65\x7d\x1d\xdb\xbb\ -\x3a\x8a\xe5\x32\x6a\xe1\x5f\xa8\x7e\x32\xd0\xa0\x42\xdf\xce\x77\ -\x77\xe3\x4a\x3c\x8e\x00\xe5\x37\x2d\x4b\x94\x6d\xc7\x39\x86\xfb\ -\xe6\x91\xdc\x4f\x33\x19\x9c\x56\x55\x5c\xd0\x34\x58\x96\x25\xc9\ -\xdc\x06\x73\x3f\xcb\xba\xf8\xfe\xfe\x35\xc6\x6f\xcf\xe0\xd6\xc0\ -\xf1\xdc\x6a\x67\x27\x62\xd1\x28\x6c\x3a\x78\xcb\x34\x45\x91\x05\ -\x98\xfb\xe7\x87\x57\xd8\x5c\x4d\x61\x63\xe5\x25\x9a\x8e\x83\xb5\ -\x6c\x16\x1b\x5b\x5b\xf8\x98\xcb\x79\x6b\x76\xce\x4b\x2e\x2f\xa7\ -\x9f\xa4\x52\xab\xcd\x03\x01\x49\x66\x0e\x56\x3b\xa3\x0d\xa1\x5a\ -\xad\xe2\x5c\xff\x10\x2c\x62\x8e\xc5\x62\xde\xae\x2a\xb5\x6b\xfd\ -\x39\x03\xe6\x56\x43\x21\x69\x6e\x76\xf6\x06\xd5\xc1\xd0\xf5\x80\ -\xcc\x1c\xac\xf6\xee\x6d\x1a\x86\x61\x60\x2d\x93\xc6\x9d\xeb\xf7\ -\x91\xa3\x9d\x7d\x2b\x45\x22\xa8\xd7\xeb\x18\x4f\x24\x50\xd3\xf5\ -\xca\xd9\x78\x7c\x21\x14\x0e\x77\x39\x86\x51\x96\x99\x83\x3b\x78\ -\xf1\x70\x9e\x52\xe7\xbd\x82\x7a\xad\x86\xab\xa3\xa3\xde\x3c\x48\ -\xcc\xbe\x71\x9e\x24\x49\xdf\xec\x7c\xfe\xf9\x1e\xc0\xe7\x5e\x11\ -\x99\x83\x3b\x60\xae\xde\x91\x91\x05\x1e\x2d\xe2\xf5\xbd\x3d\xce\ -\x79\xa4\x60\x5c\x9c\x9c\xdc\xa1\xe2\x22\x79\x03\x97\xa6\xa6\x1e\ -\xec\x9a\xa6\x5b\xa1\x57\xc5\x73\x1e\x7f\xe8\xfa\xa1\xb7\xc7\x39\ -\x8f\xe7\xe4\x88\x8d\x8d\x1d\x5c\x6d\xd7\xe0\xe0\x3d\x49\x55\xfb\ -\xab\xfb\xfb\xba\xd2\x68\x6c\x5b\x1d\x1d\x1a\xf3\xf9\x6d\xff\x1d\ -\x27\xee\x02\xf9\xe3\xf6\x7f\xe3\x14\x79\x84\xaf\xf9\x04\x6f\xc8\ -\xe3\xf6\x5a\x27\x1b\x9e\x98\xc0\x6f\x01\x06\x00\x48\xae\x45\x78\ -\x60\x4e\x1f\xe2\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ -\ -\x00\x00\x03\xef\ -\x89\ -\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ -\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x01\x68\xf4\xcf\xf7\ -\x00\x00\x00\x06\x62\x4b\x47\x44\x00\xff\x00\xff\x00\xff\xa0\xbd\ -\xa7\x93\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0b\x13\x00\x00\ -\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x03\x8f\x49\x44\x41\x54\x38\ -\xcb\x3d\x91\xdf\x4f\x5b\x75\x00\xc5\xcf\xf7\x7e\x2f\xb4\xbd\xb7\ -\xdc\x76\x8c\xfe\x80\x5b\xe4\x87\x73\xcc\x84\x8c\x9f\x0d\x83\x25\ -\x2e\x6e\x3e\x8c\x44\xb3\x64\x66\x21\x6a\x62\x8c\xba\x68\xa6\x0f\ -\xea\x93\x0f\x26\x8b\x0f\xc6\x7f\xc0\x44\x97\xc5\x2c\x53\x5f\xdc\ -\xe2\xa3\xc8\xa0\xb0\x1f\x22\x14\xc1\x6d\x08\x6d\x2d\x90\x51\xda\ -\x4b\x27\x3f\xd6\x52\xee\xbd\x50\x7a\xbf\xbd\x5f\x1f\x10\x5e\x4f\ -\xce\x39\xf9\xe4\x1c\xca\x18\x43\x4f\x4f\xdf\x23\xc4\xe2\x09\x30\ -\xc6\x38\xb1\x2c\x0b\x00\x20\xe6\xb6\xf2\x7c\xf9\xc9\x0a\x08\x63\ -\x0c\xdb\xdb\x7a\x6f\xb1\x54\x9c\x30\x0c\x03\x88\x8c\xde\x43\xb8\ -\xbb\x8f\xf7\xf6\xbe\xc4\x1f\x8c\xff\x0e\x58\x96\x85\x72\xb9\x8c\ -\x8c\xa6\xfd\x64\x59\x16\xc0\x18\xc3\xf4\xcc\x43\x5e\x2e\x97\xf9\ -\x48\x64\xcc\xa4\xe1\x70\x8f\xd6\x15\xee\x54\xa2\xd1\x28\x8e\x54\ -\x7b\x2b\x71\xee\xec\x79\xb3\xed\x64\x37\x9f\x8b\xcd\x9b\x1b\xcf\ -\x36\xf7\x23\x96\x65\x29\x8c\x31\xcc\xfc\xf5\x68\x28\x91\x4c\xf2\ -\x7c\x61\x8b\xdb\xb6\xcd\x4d\xd3\xac\x21\xa9\x95\x34\x04\x41\x40\ -\x3c\x9e\xe4\x27\x4e\x1c\x43\xd0\x1f\x00\x07\xc7\xfd\x07\xe3\xe8\ -\xec\x6c\xbf\x86\xef\xbe\xbd\xfe\x59\x7b\x5b\x98\x07\x82\xf5\xfc\ -\xe2\xeb\x03\x5c\xd7\x75\x3e\x7a\xf7\x1e\x1f\x1e\x89\xb4\x65\xff\ -\x7d\x0a\x32\x3c\x3c\x06\x70\xe0\xe7\x5b\xb7\x22\xa9\xe5\xd4\x39\ -\xc3\x30\x71\xe3\x87\x6b\xde\x50\x5d\xa8\x20\xcb\xf2\x3e\xc3\xff\ -\x1c\xd0\xb4\xec\xab\xd3\x33\x0f\xe7\x0d\xd3\xec\x38\xd0\x0e\x0c\ -\x4e\x9b\x73\x8c\xdd\xbd\xcf\x57\x32\x69\x5e\x2e\x97\xb9\xb6\xaa\ -\x4d\x70\xce\x21\x02\x00\xa5\xb4\xf8\xeb\xe0\x90\xd5\xd2\xf2\x02\ -\x8e\x56\x1f\x85\x6d\x73\xc4\x13\xc9\xde\xbd\xa2\x75\x41\xd4\x56\ -\xb3\x58\x5b\x5f\xbf\xd1\xdc\xd4\x24\x2a\x1e\x05\x2e\xa7\x13\x8f\ -\x67\x67\xe1\x74\x38\xd0\xd4\xd4\x10\x11\x76\x4c\xb3\xc6\xd0\xf5\ -\x77\x1a\x9b\x1b\xa0\x54\x29\xd0\x75\x1d\x6b\x6b\x1b\xa8\xac\xac\ -\x7c\xf3\xf1\xdf\xb3\x26\xe9\xeb\x3d\xc3\xd7\x37\x36\x90\xcb\xe7\ -\x30\x15\x1d\xc7\xc2\xe2\x22\x82\xc1\x20\x37\x4c\x5d\x68\x6d\x6d\ -\x85\xf0\xd1\xc7\x57\xfa\x8e\x78\xbd\x68\xa8\x7f\x0e\xfd\xe7\x5f\ -\x83\xa2\x28\x30\x77\x8c\xe7\x5d\x2e\x17\x00\x40\xf0\xf9\x7c\x93\ -\xef\xbd\xff\xee\x8b\xb5\x75\xb5\x7b\xaa\xaa\x22\x12\x19\x4b\x29\ -\x8a\xb2\x1c\xee\xea\x86\x57\xf1\xec\x3f\x0e\x00\x9c\x73\x00\xa0\ -\x84\x10\x4e\x08\xb1\x4b\xa5\x52\xf3\x64\xf4\xcf\x39\x87\xc3\x21\ -\xf9\x03\x35\xf0\x7a\xbd\x90\x5d\x92\xb5\xbb\xbb\x7b\xdd\xed\x96\ -\x3f\xa1\x54\xb4\x38\xb7\x0f\x0b\x08\x00\x0a\x80\x51\x4a\xb1\xb0\ -\xb0\xf8\xf9\x93\xe5\xd4\xd7\x75\x75\xb5\x50\x3c\x55\x90\xdd\x32\ -\x14\xb7\x82\x4a\xb1\x02\x7b\xa5\x22\xe6\x62\x71\x80\xe3\xc7\x8e\ -\xf6\x93\x57\x0e\x09\x00\x80\x31\xe6\x9f\x98\x8c\xfe\x51\x51\xe1\ -\x38\xe6\xf3\xd7\x40\x92\x9c\xa8\xaa\xaa\x82\xec\x92\x40\x29\x45\ -\x2e\x9f\x43\x2c\xfe\x0f\xac\x92\x05\x8f\xc7\xb3\x13\x0c\x04\xde\ -\x12\xe7\x63\x09\x08\x44\x00\x21\xe4\xed\x8c\x96\xb9\x19\x0a\x85\ -\x20\x49\x4e\xb8\x24\x17\xdc\x6e\x37\x24\xa7\x0b\x84\x10\xa4\xd3\ -\x69\x24\x17\x96\x40\x29\x85\x3f\xe0\x5b\x57\xd5\xba\x16\x4a\xe9\ -\x96\x98\xcf\xe5\x4e\xdd\xbe\xfd\xcb\x37\x53\x53\xd3\x5d\x82\x40\ -\x50\xab\xd6\xe2\x8d\x81\x01\x5c\xba\x74\x11\x94\x52\x94\x4a\x25\ -\xc4\x12\x09\x68\x99\x55\x54\x57\x57\x83\x08\x18\x5a\x5d\xd5\xfa\ -\x4d\xd3\x40\x30\x18\x84\x90\xcf\x17\xb6\x0c\xdd\x14\x08\x00\x66\ -\x31\x2c\x25\x97\x70\xf5\xea\x97\x50\xd5\x46\x74\x74\xf6\xe0\xb7\ -\x3b\x77\xa0\x6f\x1b\x08\xd5\xab\x00\xe1\xdf\xeb\xba\xde\xaf\xaa\ -\x21\xb4\x1c\x3f\x0e\x49\x92\x40\x46\x22\x63\x10\x45\x11\xb1\xb9\ -\xc4\x85\xc1\xc1\xc1\x9b\xf9\x7c\xde\x23\x08\xc2\xc1\x26\x28\x14\ -\x0a\xf8\xe0\xc3\xcb\x38\xfb\xca\x99\x2f\x4c\x73\xe7\x2b\x59\x96\ -\xd1\xd4\xd0\x08\x49\x92\x20\x08\x02\xc8\xc8\xf0\x28\x00\x80\x08\ -\x84\x1a\xe6\x4e\x59\xd7\x0d\x64\xb3\xd9\xe6\xcd\xcd\x67\x97\x19\ -\xb3\x5e\xf6\xfb\xfd\x4f\x4f\x9f\x3e\xf5\xa9\xc7\xab\xa4\x82\x81\ -\x00\xfc\x3e\x3f\x6c\xdb\x3e\x1c\xfe\x3f\x11\x5f\xc4\xbb\xcd\x16\ -\x27\xa0\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ -\x00\x00\x02\xaf\ -\x89\ -\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ -\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\ -\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\x74\x77\x61\x72\x65\ -\x00\x41\x64\x6f\x62\x65\x20\x49\x6d\x61\x67\x65\x52\x65\x61\x64\ -\x79\x71\xc9\x65\x3c\x00\x00\x02\x51\x49\x44\x41\x54\x78\xda\x9c\ -\x53\xcf\x6b\x13\x51\x10\xfe\x36\xfb\x62\x8d\x69\x48\x62\x9b\x18\ -\x8d\xab\x5d\xd3\x84\xa2\x10\xbd\x48\x0f\x62\xa9\xd8\x93\xe0\x49\ -\x0f\xa5\x20\x52\xb0\xe0\x7f\xe0\x45\x3c\xf5\xa6\x77\x7b\xe8\x55\ -\x28\x41\x3d\x78\x28\x7a\xf0\x47\x51\xa4\xbd\x58\x0d\xa8\x60\x5a\ -\x13\x51\xd0\x43\x89\x69\xf3\x63\xb3\xc9\xee\x3e\x67\x9e\xd9\xa2\ -\x08\x4a\x1d\x18\xde\xdb\x99\xf9\xbe\xf7\xcd\x7b\xb3\xda\x8d\x85\ -\x05\xb0\x69\x9a\x76\x9e\x96\xfd\xf8\xbb\x3d\x71\x1c\x67\xad\xdb\ -\xe9\xe0\xdc\xf3\x19\x48\x0a\x08\xd7\x75\xfd\xe4\x81\xeb\x93\x93\ -\x73\x0e\x7d\x73\xc2\x95\x12\x5d\xda\x77\x3d\x4f\xed\x2b\x95\x0a\ -\x1e\x15\x8b\x57\xa5\x94\x1a\xa5\x4b\x3e\x28\x30\xf1\xf8\x32\xcc\ -\xb5\x7b\x20\x56\x4d\x72\xb1\xe3\xc0\xe9\x76\xe1\xf6\xbc\xd3\x6e\ -\xc3\x6a\x36\xd1\x68\x34\x30\x3b\x35\x35\x47\xb9\xb3\x44\x92\xf5\ -\x09\x04\xfb\xf0\xa7\x07\x57\x5a\x32\x78\x41\xd3\x2e\xe1\xc5\xea\ -\x2a\x3c\x22\x8a\xc5\x62\x68\xb5\x5a\x38\x3e\x32\xa2\x0a\xab\xd5\ -\x2a\xee\x2c\x2e\x22\x9f\x4c\xde\x5e\x29\xcc\x3e\x85\x8e\x02\x85\ -\xe7\x05\xa9\x1b\x44\x40\xcf\x65\x8f\x9e\x9c\x60\x6d\x99\x4c\x06\ -\x74\x82\x22\x89\xc7\xe3\x08\xea\xba\x22\x38\x35\x3a\x8a\x0e\xa9\ -\x0b\x85\xc3\x18\x68\x5d\x3c\x23\x1f\xbe\x7a\x2d\x3d\x77\x50\xb8\ -\x12\xc6\x5e\xe3\xd8\xf0\x26\x5d\x4c\x40\xd3\x54\xaf\xd1\x68\x54\ -\x9d\xc8\x24\x1f\x89\x8c\x09\x39\xc6\x8a\x4e\xe4\xf3\xb0\x6d\x1b\ -\x49\xc2\x54\x2b\x45\x43\xb8\x1e\x0e\xed\x8e\x26\xf7\x59\x56\x1b\ -\xbf\x2a\xe0\xd3\x7d\x25\xb2\x47\xe2\x2b\xe2\x5a\xc6\x30\x96\x14\ -\xc8\xa1\x60\x38\x16\x6a\x12\x3b\x3d\x25\xca\xe5\xf2\x36\xc0\x57\ -\xc2\x2b\x7f\xb3\x82\xc3\xa9\x14\xb8\x96\x31\x8c\x15\x8e\x87\x5c\ -\x24\x65\x26\xac\xf7\x75\x94\x0b\xd7\x30\x40\xb7\xde\x97\x1b\x47\ -\x5f\x76\xec\x37\x25\xf6\x87\x25\x04\x4b\x4b\xf8\xba\xbe\x07\x56\ -\xdb\x46\xc4\x34\x13\x8c\xe5\x16\x44\x24\x91\x4e\x4d\x27\x7e\x3e\ -\x0b\x4f\xd2\xca\xf2\x7d\x38\xc2\x50\x40\x7e\x0d\x6e\x63\x73\xf9\ -\x2e\x4e\x8f\x8d\xab\x9a\x69\x53\x2d\x29\xc6\xb2\x02\xb1\xb5\xb1\ -\x41\x7d\x59\x2a\xda\x4f\x00\x23\x9d\xc6\x97\x67\x37\x15\x41\x93\ -\x62\x3c\x58\xe6\x90\x89\x66\xbd\x8e\x46\xad\xa6\xea\x42\xa1\x10\ -\x1c\x45\xe0\x4a\xe1\xf0\xf0\x90\xb3\xd5\x88\xcc\xc8\x66\x71\xd0\ -\x3c\xf2\xc7\x1c\x7f\x2e\x6d\x0f\xa0\xaa\x67\xac\xe8\x7a\x08\x76\ -\x3a\x34\x71\xe4\xbe\xad\xbf\x7d\x87\x7f\x99\xae\x0b\x30\x56\x34\ -\x6c\xf4\x4b\xc9\x5a\x74\xec\xc4\x18\xc3\x58\xf1\xe6\x9b\xac\x6c\ -\xcd\xdf\x7a\x89\xff\xb0\xf2\x77\x54\x78\x76\x76\x91\xc7\x7a\xff\ -\xc5\x4e\x8c\x2f\xad\xf6\x43\x80\x01\x00\xc1\x52\x4e\xcc\x97\x5f\ -\x6c\x5a\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ \x00\x00\x02\x77\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ @@ -461,6 +662,230 @@ qt_resource_data = "\ \x12\xe3\x03\xfa\xf1\xc0\xf9\xeb\x4d\xf9\x37\x2f\x04\xe0\x8f\x00\ \x03\x00\xe7\xe3\x7a\x6e\x30\xbb\xf3\xb7\x00\x00\x00\x00\x49\x45\ \x4e\x44\xae\x42\x60\x82\ +\x00\x00\x06\x71\ +\x89\ +\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ +\x00\x00\x90\x00\x00\x00\x90\x08\x02\x00\x00\x00\x68\x24\x75\xef\ +\x00\x00\x00\x03\x73\x42\x49\x54\x08\x08\x08\xdb\xe1\x4f\xe0\x00\ +\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0e\xc4\x00\x00\x0e\xc4\x01\ +\x95\x2b\x0e\x1b\x00\x00\x06\x14\x49\x44\x41\x54\x78\x9c\xed\x9d\ +\x4b\x76\xa3\x30\x10\x45\x71\x8e\x07\xc9\x72\xb2\x9d\x64\x7d\xde\ +\x4e\xb2\x1c\x0f\xe9\x01\x69\x2c\x0b\xa1\x6f\x7d\x5e\x95\xf5\x66\ +\xdd\xb1\x41\xd4\xd5\xe5\x23\x0b\xb8\x7c\xbd\xff\x2c\xcb\x72\xbb\ +\x7f\x2e\x18\xf9\xfe\xf8\xd5\x6e\x42\x1c\xa8\xe2\x5c\x36\x60\x5b\ +\xa0\x5a\xa6\xdd\x84\x47\x10\xca\xb2\x17\xe4\xb2\xae\x6b\x54\x1d\ +\x84\xf6\x6d\x01\xc1\xa6\x5b\x90\xa8\x08\xd7\xb3\x4f\x20\x60\xdb\ +\xda\x00\x82\x4d\x3e\xc7\x0d\xbf\xdd\x3f\x2f\xeb\xba\x26\xff\xb6\ +\x7f\x82\xbd\x5d\x75\x51\xc4\x26\x5f\x84\x0c\x8e\x84\x61\xc7\x6f\ +\x22\x60\x7b\x11\xdb\x32\x1b\xb8\x55\xe0\xcf\xb0\xfc\x47\xc3\x2f\ +\x20\x44\x18\x9b\xcc\x86\x57\xd6\xbf\x60\xd8\x71\x89\x08\xd8\x9c\ +\xd9\x56\xb3\x21\x7b\xd9\x1f\x86\x55\x7e\x33\xfa\xbe\x7a\x04\xb0\ +\xf1\x6d\x6c\x47\xc1\x1b\x0c\x3b\xae\x09\x01\x9b\x51\xdb\x9a\x1a\ +\x1c\xd6\xf9\xc9\xb0\xd6\x05\x1d\x17\xa7\x1b\x26\x6c\xb4\x1b\x38\ +\x58\xe1\x4e\xc3\x8e\x2d\x40\xc0\x06\x6e\x5b\x5f\xc3\xa2\xc2\xbe\ +\xe5\xff\xdc\xd4\x1a\x90\x4a\x21\x74\x9d\x28\x84\xc5\x21\x30\x2c\ +\x8c\xba\x6d\x61\x5d\x6e\xf7\x4f\xf5\x3e\x34\xd8\x80\x63\x25\x13\ +\xc0\xc6\xb7\x53\x05\x5b\xb2\xcd\x8a\x3b\x49\xa6\x95\x12\x1b\x16\ +\x46\x0c\x5b\xe5\x25\xa7\x18\x36\xaa\x15\x25\x4b\x97\x06\x46\xb8\ +\x33\x61\xc5\xd6\x71\x72\xcc\x8a\x4d\xa0\x4f\x30\x1a\x16\x86\x1c\ +\x5b\x77\x69\x98\xb0\x91\x2f\xf0\xac\x56\xa7\xc0\x38\x8e\xd8\x24\ +\xd8\x48\x5a\x45\x88\x4d\xf8\x00\x29\x64\x58\x98\x6e\x6c\x4c\xbd\ +\xb8\x7b\xb1\x7c\xa8\x32\xc5\xc9\x01\x63\x3d\x2d\x6e\xc2\xc6\xda\ +\x8b\x3b\xb0\x29\x5e\x2d\x28\x18\x16\xa6\x88\x4d\xac\x34\x95\xd8\ +\xd4\xc7\x9a\x0b\xc0\x64\xae\x3d\x93\xd8\x54\x7a\x71\x06\x9b\xfa\ +\x35\xf8\x96\x78\xf0\xf7\x18\xf9\x5f\x0b\x59\xaf\x63\xea\xa3\xd8\ +\x63\x32\x89\xc7\x12\x3b\x16\x41\x1b\x90\x8e\xbc\x40\x8e\x49\x2e\ +\x35\xc0\xe4\x83\x50\x29\x95\xb1\xec\x9a\x0d\xaf\x02\x26\x5f\xc1\ +\xdb\xfd\x53\x0b\x1b\xce\xcf\x0e\xc9\x28\x9f\x25\xe6\x63\x74\x0c\ +\xb0\x2f\x95\x1d\xb4\x76\x97\xa8\xb8\x9b\x12\xb0\x0d\xdc\xaa\x30\ +\xd0\x86\x85\xb1\x32\x06\xd8\x97\xfa\x1e\xd9\x70\xd2\x81\x70\x2e\ +\x40\x68\x9b\x21\xab\xc2\x98\x31\x2c\x0c\xec\x18\x60\x5f\x9a\xba\ +\x60\xdb\x69\x3d\x82\x64\x7b\x3a\x6c\x33\x6a\x55\x18\x93\x86\x85\ +\xc1\x19\x03\xec\x4b\x6b\x9f\x6b\xbe\x70\x86\x92\x6c\x4f\xc6\x36\ +\x07\x56\x85\x31\x6f\x58\x98\xc8\x36\x7c\x4e\x1d\xbd\xbf\x67\x68\ +\x0a\x53\xb2\x3d\xe0\xcd\x1b\x8c\x2b\xc3\x16\x0b\x56\xed\xe9\xeb\ +\x58\x9d\x83\xbf\x80\xbd\xd8\xd9\xb1\xea\x2c\x1e\x0c\xb3\xc8\xa9\ +\xbb\xc7\xf7\xff\xbc\x82\x20\xd9\x8b\x58\x15\xc6\xaa\x61\xa6\x39\ +\x8d\xf4\xf5\xa1\x1f\x30\x55\x24\x7b\x41\xab\xc2\x58\x32\xcc\x07\ +\xa7\xc1\x5e\x3e\x3a\x45\x40\xec\x0e\x7b\x1f\xb4\xc6\x83\x6e\x98\ +\x33\x4e\xe3\xfd\x9b\x60\x12\x0e\xdf\x9d\x29\xce\x68\x91\x04\xd1\ +\x30\xaf\x9c\x48\x7a\xf6\xd5\x6b\x75\xbc\x06\xd1\x30\xb4\x8c\x9b\ +\x41\x78\x77\x24\xd9\x44\x52\x84\x81\x0f\xa6\xd0\xde\x8c\x3a\x18\ +\x1a\x60\x8e\x69\x8d\x87\x96\x37\xe5\x54\x6d\xc7\xd8\x70\x24\xc3\ +\x3d\xad\xf7\x11\x72\xd2\xc4\x37\x43\x38\x86\x07\x22\x99\x8d\xa1\ +\x29\xa3\xe1\x60\x4c\x7f\xbb\x91\x63\x84\x08\x92\xd9\xfb\x79\xc5\ +\x4a\x98\xe8\xb2\xdc\xd0\xe7\x18\xa4\xba\x64\xb6\xa7\x08\xc0\x86\ +\x8f\x2b\xd7\x2d\xb3\x8e\x71\xea\x4a\xe6\x67\x9a\x1b\x4e\x58\x89\ +\x32\xde\x94\xee\x18\xaa\xa2\x64\x0e\xa7\x6a\xeb\x86\x9b\x25\xef\ +\x63\x1f\x1c\xa3\xd5\x92\xcc\xc9\xed\x46\x20\x11\xa0\xc8\xfe\x60\ +\x15\xc7\x80\x55\x24\x33\x7c\xcb\x2c\x5a\x64\xf8\x49\x3c\xba\xc8\ +\x31\x66\x79\xc9\x8c\x3d\xf6\x01\x36\x62\xe4\x84\x1e\x0e\xe6\x18\ +\xb6\xb0\x64\x4f\x6f\x99\xcd\x04\x67\xe6\xd0\x8b\xa7\x16\xd8\x0c\ +\x48\xe6\xbc\x44\x9a\x88\xed\x81\x44\x9f\x97\x38\x8f\x64\xe3\x91\ +\x7b\x84\xac\x63\x5a\xe3\xa1\x3f\xad\x9f\xd8\x8a\x91\x91\xac\x00\ +\x6c\x72\x12\x08\xd7\xd0\xd4\x84\x57\x8c\x80\x64\x39\x60\x93\x90\ +\x40\x78\x7f\x5e\x99\x08\x8b\xe1\x96\xec\x14\xd8\x64\x23\x10\x89\ +\x29\x02\x13\x64\x31\xac\x92\xa5\x81\x4d\x2a\x02\x91\x9b\xe6\x36\ +\x71\x16\xc3\x27\x59\x02\xd8\xe4\x21\x10\xe9\xa9\xda\x13\x6a\x31\ +\x4c\x92\x91\xbd\xda\x9e\x69\x39\x2e\xa3\x73\xbb\xd1\x44\x5b\x0c\ +\x87\x64\x4f\xc0\x26\x03\x81\x68\xde\x32\x3b\x01\x17\x43\x2e\xd9\ +\x03\xd8\xac\xbe\x40\xf4\x1f\xfb\x30\x31\x17\x43\x2b\xd9\x1f\xb0\ +\x59\x77\x81\xa0\x3c\xba\x68\xc2\x2e\x86\x50\xb2\xb7\x65\x56\x5c\ +\x24\x54\xcc\x2e\x5f\xef\x3f\x24\x85\x9e\xf3\x44\x65\x52\x7e\x53\ +\x7a\x4d\xbc\xd2\x22\x7c\x6d\xfb\x42\xb4\x07\x42\x7c\xf1\x36\x42\ +\x38\x5e\x6d\x4b\xc2\x9e\x60\xe6\xaf\x33\xbd\xc0\x8f\xc4\xd3\xb0\ +\x47\x64\x5e\x18\x3d\xb8\x84\x51\xc3\x7c\xe8\x05\x6e\x55\x98\x57\ +\x37\x4c\xc0\xaa\x28\x83\x5d\x7c\xc8\x30\xd3\x7a\x19\xb2\x2a\xcc\ +\x2b\x1a\x26\x6f\x55\x94\x91\x8e\xde\x6f\x98\x45\xbd\x8c\x5a\x15\ +\xe6\x55\x0c\x53\xb7\x2a\x4a\x77\x77\xef\x34\xcc\x90\x5e\x50\x9c\ +\xc6\xe3\xdc\x30\x64\x5a\x72\x13\x49\xf1\xf5\xda\x39\xf9\x7b\xa7\ +\x95\x37\xc3\x92\xc7\x2a\x58\x6c\x1d\xad\x6a\x3e\x86\x61\x6e\xf9\ +\x52\xb1\xf7\xdb\x5a\x8e\xbc\x93\xac\x89\x07\xc3\x9a\xce\x00\xd1\ +\x6c\x6b\x6d\x4c\x9b\x61\x50\x9b\xba\x0c\xe8\x62\xd7\x36\xab\x86\ +\x91\x5c\x57\x81\xd8\xd6\xd4\x86\x06\xc3\x10\xb6\x6d\x61\xd0\xc2\ +\x96\x6d\x96\x0c\x63\x1d\xad\xd0\xb5\xad\x7e\xd5\xb5\x86\xe9\xea\ +\x25\xd6\xfd\xf1\x6d\x43\x37\x4c\x65\x0c\x50\xc5\xb6\xca\x35\x56\ +\x19\xa6\xa2\x97\x7a\x37\x07\x39\x66\x47\x01\x35\x4c\x9d\x96\x4a\ +\x6a\xba\x48\x19\x98\x64\x47\x43\x1b\x03\xdc\x76\xc8\x50\xbd\x07\ +\xe5\x01\x97\xc9\xa2\x28\x9e\x02\x44\x2b\xdd\xfe\x29\xd0\x87\xbe\ +\x3f\x7e\xf3\xdb\x5b\x00\x26\xd0\x44\xb4\x31\xc0\xcc\x8a\xc4\xb0\ +\x65\xa2\x69\x58\x13\x03\x01\x6c\x95\x0b\xe7\xc6\x96\x97\x2c\x07\ +\x8c\xaf\x4d\x68\x63\x80\x1d\x0b\xd4\xb2\x4d\xda\x30\xc2\x3b\x65\ +\x48\x16\x35\xb8\x10\x26\x6c\x19\xc9\x4e\x81\x91\x37\x02\x6d\x0c\ +\x90\xb0\x3d\x92\xb6\x49\x18\xc6\x7a\xe0\xe9\xc0\xc6\xd4\x1e\x5a\ +\x6c\x67\x92\xa5\x81\x51\xad\x15\x6d\x0c\x50\xa0\x3d\xdc\xb6\x71\ +\x19\xa6\x72\xf1\x94\xc1\x26\xdc\x1e\x12\x6c\x49\xc9\x12\xc0\x06\ +\x57\xa3\x3e\x2e\x10\xb5\x5f\xb1\x3d\x1c\xb6\x51\x8e\x25\xa2\x8d\ +\xe2\x2c\x00\xbd\x67\x19\x2b\xcb\x11\x76\x6c\x58\x5f\x77\x40\xa8\ +\x4b\x32\x38\xbf\x6f\x51\xd9\x16\xdf\x94\xde\xba\x44\xcc\x1b\x81\ +\x93\x41\xc0\xb6\x65\xa4\xc8\x4f\x86\x35\x2d\x08\x67\xfb\x2b\xe3\ +\xc3\xb6\x27\xc3\x2a\x17\x21\x70\x5d\xc5\x1d\x04\x6c\x5b\x5a\x6b\ +\xfe\x30\xac\xe6\x9b\x38\xdb\x39\x18\xbb\xb6\x3d\x0c\xcb\x7f\x47\ +\xf8\x12\x58\x32\x08\xd8\xb6\xd4\x20\xb8\x16\x3f\x8a\xb3\x3d\x4c\ +\xb1\x65\xdb\x9f\x61\xc9\x0f\x29\x8e\x56\x68\x05\x01\xdb\x96\x33\ +\x22\xd7\xe4\xdf\x70\xda\x2d\x1c\x7c\xdb\x2e\xeb\xba\x86\xff\xab\ +\xde\x56\x84\xb9\x37\x5b\xd4\x4b\xb1\x27\xac\xc9\xe3\xb5\xc0\x20\ +\xed\xc3\x01\xb6\x05\xa4\x2c\xcb\xff\xca\xfc\x03\x0c\x3a\xb7\xd7\ +\x9d\x1e\xca\x90\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ +\ +\x00\x00\x07\x3c\ +\x89\ +\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ +\x00\x00\x18\x00\x00\x00\x18\x08\x06\x00\x00\x01\x97\x70\x0d\x6e\ +\x00\x00\x00\x06\x62\x4b\x47\x44\x00\xff\x00\xff\x00\xff\xa0\xbd\ +\xa7\x93\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0b\x13\x00\x00\ +\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x06\xdc\x49\x44\x41\x54\x48\ +\xc7\x55\x95\x59\x6c\x54\xd7\x19\xc7\xff\xe7\x2e\x73\x77\xcf\x6e\ +\x7b\xc6\x0e\x60\x30\x13\xb1\x04\x12\x07\x95\x94\x90\x1a\xda\x4a\ +\x49\x53\x35\x54\xb4\x52\x9a\x86\xb4\x4d\xe9\xf2\xd4\x28\x6d\x43\ +\x15\xa9\x2f\x51\x93\x80\xd2\x2c\x28\x52\x15\xa9\x8d\x68\x95\xbc\ +\x44\x6d\x1a\x51\xd2\x46\x4d\xd8\x8c\xed\xb8\x40\x69\x4b\xd8\xed\ +\x19\xb0\xc7\x60\x7b\x6c\x33\x1e\xcc\xc0\x8c\x67\xee\xb9\xe7\x7c\ +\x7d\x18\x6c\xc8\x79\x3c\xba\xff\xef\xf7\x2d\xff\xef\x1e\x46\x44\ +\x08\x82\x00\x35\xbf\xbe\x16\x44\x84\xcf\x4e\x9d\xa1\x6a\x6d\x8e\ +\x40\x44\xe8\xe8\xc8\xd0\xa9\x33\xa7\x09\x9c\x73\xf4\xf7\x0f\xbc\ +\xc2\x39\x07\xab\xd7\xeb\xb8\x59\xad\xd0\xa9\xcf\xce\x40\x79\x60\ +\xfd\x43\x64\x1a\x26\xda\xda\xd3\x0c\xb7\xa2\x85\xa7\xaf\x16\xbb\ +\x87\x72\x59\xf2\x7d\x9f\xfe\x75\xec\x18\x29\x00\xd0\xdb\xd7\x3f\ +\x1b\x08\x7e\x64\xe9\x92\x0e\x1c\x3c\x74\x08\xba\xa6\x6d\x54\x00\ +\xa0\xe7\x50\xdf\xef\xbf\xfb\xf8\xf7\xf0\xe0\x86\x4d\x88\x44\x23\ +\x6c\x5d\x57\xd7\x00\x82\x20\x40\x10\x04\x10\x42\xe0\xe4\xc9\x53\ +\xef\x07\x41\x00\xce\x39\x10\x04\x01\xe3\x9c\xe3\x70\x4f\x2f\x15\ +\xa6\x26\x89\x73\x4e\x44\x04\x85\x31\x46\xfd\xfd\x03\x57\x97\x74\ +\x2c\x42\x22\x16\x47\xff\xc0\x00\xc6\xc6\x26\xb6\xb3\x6c\x2e\x07\ +\xd7\x75\xc9\x75\x5d\x10\x49\x9c\x3c\x79\x1a\x89\x64\x8c\xb1\xf5\ +\x5f\xd8\x48\xb3\xb3\xb3\xf0\x03\x1f\x6f\xbd\xf5\x3b\x28\xaa\xa2\ +\xdc\x7b\xef\x1a\x52\x5e\xda\xf9\x1b\x16\x8f\xc7\x91\x6e\x49\x61\ +\xa6\x58\x44\x24\xd2\x44\xc9\x78\x02\x8c\x88\x20\x84\x00\x11\x31\ +\xc6\x98\x02\x40\x9c\x3b\x7f\x61\xa0\x52\x99\xdb\x90\x6a\x6b\x46\ +\xba\x35\x8d\xf2\xf5\xeb\xc5\x58\x2c\x96\x94\x52\x40\xc1\xad\xc3\ +\x18\x23\x55\x55\xc5\x3f\x3e\xfa\x27\x01\x6c\xc3\x5d\x8b\xd2\x48\ +\xc6\x93\xd0\x35\x0d\x60\x48\x1c\xea\x39\x42\xa3\xa3\x57\x9e\x66\ +\x44\x84\xb3\xe7\x06\x11\x08\xfe\x66\xf9\x7a\xf9\x99\x74\x3a\x05\ +\xaf\xc9\x45\xc4\x0b\x43\xd3\x35\x0c\x65\xb3\x18\x1f\x2f\x20\x95\ +\x6e\xc5\xdd\x99\x0c\x63\x44\x84\x07\xd6\x7f\x89\x0a\x93\x13\x10\ +\x42\x62\xd7\xae\x97\xf1\xd4\x53\x4f\x80\x73\x8e\x81\xa3\x47\xe1\ +\xd7\x39\xc2\x61\xef\x52\xe9\x5a\xa9\xb3\xab\xab\xab\x91\xd2\x93\ +\xdb\x9e\x78\x36\x9d\x4a\xa3\xb5\xa5\x05\xaf\xbd\xfa\x3a\x56\xaf\ +\xee\x42\x26\xb3\x0a\x9e\xe7\xc1\xf5\xec\xfd\x3e\xf7\x3b\xd3\xe9\ +\x74\x23\x75\x22\xc2\x81\xfd\x3d\xf0\xb9\xcf\xc6\xc6\xc7\x9b\x4e\ +\x9c\x38\xf1\xb7\x2b\x57\xc6\x36\xc9\x40\xe0\x97\x3b\x9e\xfd\x51\ +\xba\x2d\xb5\x67\xd5\x8a\x95\x60\x8c\x35\x8a\x9d\x9f\xdd\xfc\xcc\ +\x38\xe7\x98\x9e\x9e\x5e\xdf\xd3\xd3\x77\x64\x24\x9f\x7f\x4e\x92\ +\x9c\xbf\x67\x44\x74\xbb\x4b\x00\x34\xc6\x18\x14\x45\xd1\xff\x77\ +\xf2\xf4\xb1\x44\x32\xde\xed\x78\xee\xab\x22\x10\x54\x2a\x95\xbe\ +\x09\x80\x01\x58\x10\x38\x00\x02\x55\x55\xf1\xf1\x27\x07\x2b\xf3\ +\x9d\x32\x42\x21\x28\x8a\x82\x64\x32\xb9\xf7\xe2\xa5\xe1\x67\xee\ +\x14\x54\x1a\xed\x3d\xff\x46\x34\x1a\xd1\x4d\x2b\x04\xdb\xb6\x61\ +\x99\x16\x82\x20\xc0\xb1\x7f\x1f\xc7\xf0\x70\x7e\x37\x00\x28\x8c\ +\x31\x30\xc6\x30\x57\xab\x3f\x34\x3a\x7a\xf9\xe7\x91\x68\x04\x8e\ +\xe3\xc0\x73\x1c\xa8\x8a\x82\x89\xc2\x04\xaa\x95\x1a\x9a\x9b\x93\ +\x8d\xbc\xcf\x5f\xc8\x82\x31\x86\x5c\x2e\xdb\x77\x77\x26\x03\xc7\ +\xb1\xe0\xd8\x0e\x34\x4d\x47\xad\x56\x43\x36\x77\x11\x9a\xaa\x63\ +\x79\x66\xd9\x63\x00\xa0\x08\xc9\x01\x12\x1f\x64\x32\xcb\x11\x8d\ +\x47\x60\xdb\x36\x0c\xd3\x00\x63\x0c\x17\x86\x06\x21\x85\x84\xeb\ +\xba\xf5\x42\xa1\xf0\x77\x00\x50\x86\x2e\x64\x7f\xf6\xad\xad\x8f\ +\x6f\xdd\xbc\xe9\xab\xd8\xf6\xe4\xd3\xf0\x1c\x0f\x66\xc8\xc0\x4c\ +\x69\x06\xe3\x63\x05\xc4\x62\x71\x5c\x9b\x9d\x59\x5c\x2c\x16\x01\ +\x00\xca\xd4\xe4\x74\x53\x2c\x16\x43\x32\x99\x44\x3e\x9f\x47\x5b\ +\x7b\x07\xde\x7b\xef\xcf\xb8\x34\x3c\x8c\x58\x3c\x8e\x20\xe0\x7d\ +\x8c\xa9\x53\x2b\x57\xae\x68\x08\x56\xac\x5e\xf1\x32\x63\x0c\xae\ +\xe3\xa2\xc9\xf5\x90\x6a\x69\xc5\x0b\x2f\xbc\x88\xe3\x47\x4f\x20\ +\x91\x88\x22\x10\xbc\xdb\x30\x34\x08\x21\x1b\x02\x92\x12\x3b\x7e\ +\xb5\xc3\xf6\x3c\x0f\x9e\xe7\x21\x1a\x8d\x20\x16\x8d\xe1\x8f\x7b\ +\xde\x41\xad\x56\xfb\x83\xae\xeb\xb8\x6f\xed\x7d\x48\xc4\xe2\x0d\ +\x01\x24\x83\xe7\x3a\xf5\xed\x3f\xde\xce\xba\x37\x77\x6f\x8f\x27\ +\x13\x81\xeb\x79\x58\x75\xcf\xaa\xaa\xae\xeb\x3f\x4d\xb5\xa6\xe0\ +\x79\x1e\x88\xe8\xb6\xf9\x84\x10\x0b\xfe\x20\xa2\x79\xa3\x31\xba\ +\xf5\x95\xaa\xaa\x28\x16\x67\xbe\x76\xfa\xf4\x99\x0f\x6e\x56\x2a\ +\x56\x3c\x1e\x43\x3c\x11\x87\xeb\x3a\x50\x15\x65\x4e\x53\xb5\xb7\ +\x2d\xcb\xda\x6d\xdb\x76\x1e\x00\xa4\x24\x00\x04\x5d\xd7\x3f\x0f\ +\x20\x22\x0d\x40\x2b\x63\xac\x00\x40\xcc\x07\x1f\x1e\xc9\xbf\x74\ +\x61\x30\xfb\xeb\x48\xb8\x09\xae\xeb\xc2\x71\x6c\x84\x0c\x1d\x96\ +\x65\xc1\xb5\x1d\x18\x86\x01\x45\x51\x50\x2e\x97\xe9\xc6\x8d\x1b\ +\x6f\xa4\x52\xa9\xe7\x18\x08\x4c\x51\x3f\x07\x60\xb7\x8b\x68\x54\ +\xc1\x39\xf7\x8e\x1e\x3d\xfe\x69\xdd\xf7\xd7\xc4\xe3\x71\x58\xb6\ +\x09\xc7\xb1\x61\x18\x06\x4c\xd3\x84\x63\xdb\xd0\x35\x1d\x44\x04\ +\xdf\xf7\x31\x39\x35\x85\xfc\xe8\x28\xaa\x95\xb9\xe2\x97\x37\x6f\ +\xba\xdf\xb2\xcc\xcb\x77\x6e\x0f\x01\x20\xc6\x18\x34\x4d\x43\xa9\ +\x74\xed\x91\x8f\x3f\x39\x50\x56\x35\x6d\x4d\x7b\x7b\x3b\x22\x91\ +\x08\x3c\xcf\x85\x69\x99\x70\x1c\x07\xae\x6d\x43\x57\x35\x10\x11\ +\xaa\xd5\x2a\x86\xf3\x23\x18\x1e\xc9\x43\x04\x12\x9e\xe7\x25\x86\ +\x86\xb2\x3f\x04\x00\x0d\x00\xa6\xa6\xa7\xef\xa0\x50\xe8\xf2\xe8\ +\xd8\x87\x53\x53\x57\x1f\x6e\x6f\x6f\x87\x63\x3b\x30\xcc\x10\x42\ +\xa6\x0e\x33\x64\xc0\x32\x2d\x84\x42\x21\xa8\x8a\x02\x41\x84\x72\ +\xb9\x8c\x5c\xee\x22\x2a\x95\x2a\x84\x10\x30\x8c\x10\x16\x2f\x59\ +\x34\xd6\x96\x4a\xed\x59\x00\x14\xaf\xce\x34\x16\x49\xd3\xd6\x15\ +\x26\x27\x07\x38\xe7\xa1\x8e\x25\x8b\x61\x58\x06\x4c\x33\x84\x50\ +\x28\x04\xc3\x34\x61\x19\x06\x74\x4d\x87\xa2\x28\x08\x84\x40\x71\ +\xa6\x88\xa1\xa1\x1c\x38\x0f\x40\x44\x08\x87\xc3\x88\x46\x23\x7d\ +\x86\x11\xea\x9e\x29\x95\xd0\xd2\xdc\xdc\x00\x98\xa6\x89\xc1\xc1\ +\xec\xf3\xbd\xbd\xbd\xbb\x84\x94\x58\xbc\xe8\x2e\x68\xba\x8a\x75\ +\xeb\xee\x47\x93\xd7\x04\x92\x12\x44\x8d\x87\x4f\x61\x0a\x38\xe7\ +\x18\x9f\x2c\x20\x37\x94\x03\x11\x60\x9a\x16\xa2\xd1\x08\xca\x37\ +\xae\xbf\x52\x98\x9c\x78\x7e\xa2\x30\x0e\xcb\xb2\x6e\x03\xde\xff\ +\xcb\x5f\x3f\xdc\xbb\x77\xdf\x37\x18\x63\xa8\xd5\x6a\xf0\xb9\x0f\ +\x9f\x73\x54\x2a\x15\xd4\xeb\x75\x6c\xdc\xf8\x20\x76\xee\x7c\x11\ +\x6b\xd7\xdc\x03\x22\x42\x6e\xf0\x22\x2e\x5d\x1a\x5e\x08\x6c\xd9\ +\x26\x8a\xc5\xe2\x4f\x82\x80\xbf\x1d\x0a\x85\xd0\xd9\xd9\x09\xcb\ +\xb2\x6e\xef\xc1\xbe\x7d\x1f\xb5\xbf\xfb\xce\xbb\xff\x19\x1f\x9f\ +\x68\x99\x87\x08\x21\x20\x84\x80\x94\x12\x04\x42\xcd\xf7\x51\x9b\ +\x9b\xc3\xf7\x7f\xb0\x0d\x5b\xb6\x3c\xd6\xf8\xfd\x47\x9a\x00\x10\ +\x4a\xa5\x6b\x0f\x83\xb1\xfd\x52\x4a\x2c\x5d\xda\x81\x70\x53\x13\ +\xa4\x94\x68\x4e\x24\x1b\x80\x83\x87\x7a\x00\xc6\xd0\x7b\xb8\xf7\ +\xf5\x9e\xc3\x47\x7e\x21\xa4\x80\xaa\xaa\x90\x52\x2e\x40\xa4\x94\ +\x20\x49\x98\xab\xcd\x21\x95\x4e\x61\xf7\x9b\xaf\x41\xd7\x35\xbf\ +\x58\x2c\x7e\xc5\x34\xcd\x4f\x89\x08\xaa\xaa\xc2\x73\x3d\xa4\xd3\ +\x69\xa8\xaa\x0a\xdb\xb2\x6e\x3d\x23\x07\x7a\x1a\x0e\x92\x84\xd9\ +\xd9\x59\x0c\x8f\x8c\x7c\xe7\xec\xd9\xb3\xbf\x9d\x9a\x9c\xba\x2b\ +\x08\x02\xa8\xaa\x0a\xc6\xd8\xc2\x42\x56\xab\x73\xd8\xfa\xed\x2d\ +\x37\x1e\xfd\xfa\x23\x5f\x94\x52\x9e\xe3\x9c\xa3\xb5\xa5\x15\x6d\ +\xe9\xf4\xc2\xac\x00\x40\x51\x94\x5b\x15\x1c\x3c\x3c\xef\x52\x95\ +\x88\x70\xbd\x7c\x53\x08\x21\x50\xab\xd5\x50\x2a\x95\x50\xaf\xfb\ +\xeb\x54\x55\x79\x94\x29\xc8\x44\xc2\x91\xd9\x65\xcb\x96\xfe\x29\ +\x12\x0d\xff\x57\x51\x19\x34\x55\x43\xa6\x73\x39\x74\x5d\x87\x94\ +\x12\x77\x1e\x45\x51\xf0\x7f\x60\x84\x69\x65\x48\xcf\xfa\x14\x00\ +\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ \x00\x00\x07\x65\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ @@ -582,319 +1007,60 @@ qt_resource_data = "\ \x58\x7c\x1a\x1e\x38\x0b\x0d\x25\x78\x2f\x0c\x5b\x2c\xf0\xfe\x0f\ \xa4\xa4\xa5\x79\xe8\x4b\xcf\x5e\x00\x00\x00\x00\x49\x45\x4e\x44\ \xae\x42\x60\x82\ -\x00\x00\x07\x3c\ -\x89\ -\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ -\x00\x00\x18\x00\x00\x00\x18\x08\x06\x00\x00\x01\x97\x70\x0d\x6e\ -\x00\x00\x00\x06\x62\x4b\x47\x44\x00\xff\x00\xff\x00\xff\xa0\xbd\ -\xa7\x93\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0b\x13\x00\x00\ -\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x06\xdc\x49\x44\x41\x54\x48\ -\xc7\x55\x95\x59\x6c\x54\xd7\x19\xc7\xff\xe7\x2e\x73\x77\xcf\x6e\ -\x7b\xc6\x0e\x60\x30\x13\xb1\x04\x12\x07\x95\x94\x90\x1a\xda\x4a\ -\x49\x53\x35\x54\xb4\x52\x9a\x86\xb4\x4d\xe9\xf2\xd4\x28\x6d\x43\ -\x15\xa9\x2f\x51\x93\x80\xd2\x2c\x28\x52\x15\xa9\x8d\x68\x95\xbc\ -\x44\x6d\x1a\x51\xd2\x46\x4d\xd8\x8c\xed\xb8\x40\x69\x4b\xd8\xed\ -\x19\xb0\xc7\x60\x7b\x6c\x33\x1e\xcc\xc0\x8c\x67\xee\xb9\xe7\x7c\ -\x7d\x18\x6c\xc8\x79\x3c\xba\xff\xef\xf7\x2d\xff\xef\x1e\x46\x44\ -\x08\x82\x00\x35\xbf\xbe\x16\x44\x84\xcf\x4e\x9d\xa1\x6a\x6d\x8e\ -\x40\x44\xe8\xe8\xc8\xd0\xa9\x33\xa7\x09\x9c\x73\xf4\xf7\x0f\xbc\ -\xc2\x39\x07\xab\xd7\xeb\xb8\x59\xad\xd0\xa9\xcf\xce\x40\x79\x60\ -\xfd\x43\x64\x1a\x26\xda\xda\xd3\x0c\xb7\xa2\x85\xa7\xaf\x16\xbb\ -\x87\x72\x59\xf2\x7d\x9f\xfe\x75\xec\x18\x29\x00\xd0\xdb\xd7\x3f\ -\x1b\x08\x7e\x64\xe9\x92\x0e\x1c\x3c\x74\x08\xba\xa6\x6d\x54\x00\ -\xa0\xe7\x50\xdf\xef\xbf\xfb\xf8\xf7\xf0\xe0\x86\x4d\x88\x44\x23\ -\x6c\x5d\x57\xd7\x00\x82\x20\x40\x10\x04\x10\x42\xe0\xe4\xc9\x53\ -\xef\x07\x41\x00\xce\x39\x10\x04\x01\xe3\x9c\xe3\x70\x4f\x2f\x15\ -\xa6\x26\x89\x73\x4e\x44\x04\x85\x31\x46\xfd\xfd\x03\x57\x97\x74\ -\x2c\x42\x22\x16\x47\xff\xc0\x00\xc6\xc6\x26\xb6\xb3\x6c\x2e\x07\ -\xd7\x75\xc9\x75\x5d\x10\x49\x9c\x3c\x79\x1a\x89\x64\x8c\xb1\xf5\ -\x5f\xd8\x48\xb3\xb3\xb3\xf0\x03\x1f\x6f\xbd\xf5\x3b\x28\xaa\xa2\ -\xdc\x7b\xef\x1a\x52\x5e\xda\xf9\x1b\x16\x8f\xc7\x91\x6e\x49\x61\ -\xa6\x58\x44\x24\xd2\x44\xc9\x78\x02\x8c\x88\x20\x84\x00\x11\x31\ -\xc6\x98\x02\x40\x9c\x3b\x7f\x61\xa0\x52\x99\xdb\x90\x6a\x6b\x46\ -\xba\x35\x8d\xf2\xf5\xeb\xc5\x58\x2c\x96\x94\x52\x40\xc1\xad\xc3\ -\x18\x23\x55\x55\xc5\x3f\x3e\xfa\x27\x01\x6c\xc3\x5d\x8b\xd2\x48\ -\xc6\x93\xd0\x35\x0d\x60\x48\x1c\xea\x39\x42\xa3\xa3\x57\x9e\x66\ -\x44\x84\xb3\xe7\x06\x11\x08\xfe\x66\xf9\x7a\xf9\x99\x74\x3a\x05\ -\xaf\xc9\x45\xc4\x0b\x43\xd3\x35\x0c\x65\xb3\x18\x1f\x2f\x20\x95\ -\x6e\xc5\xdd\x99\x0c\x63\x44\x84\x07\xd6\x7f\x89\x0a\x93\x13\x10\ -\x42\x62\xd7\xae\x97\xf1\xd4\x53\x4f\x80\x73\x8e\x81\xa3\x47\xe1\ -\xd7\x39\xc2\x61\xef\x52\xe9\x5a\xa9\xb3\xab\xab\xab\x91\xd2\x93\ -\xdb\x9e\x78\x36\x9d\x4a\xa3\xb5\xa5\x05\xaf\xbd\xfa\x3a\x56\xaf\ -\xee\x42\x26\xb3\x0a\x9e\xe7\xc1\xf5\xec\xfd\x3e\xf7\x3b\xd3\xe9\ -\x74\x23\x75\x22\xc2\x81\xfd\x3d\xf0\xb9\xcf\xc6\xc6\xc7\x9b\x4e\ -\x9c\x38\xf1\xb7\x2b\x57\xc6\x36\xc9\x40\xe0\x97\x3b\x9e\xfd\x51\ -\xba\x2d\xb5\x67\xd5\x8a\x95\x60\x8c\x35\x8a\x9d\x9f\xdd\xfc\xcc\ -\x38\xe7\x98\x9e\x9e\x5e\xdf\xd3\xd3\x77\x64\x24\x9f\x7f\x4e\x92\ -\x9c\xbf\x67\x44\x74\xbb\x4b\x00\x34\xc6\x18\x14\x45\xd1\xff\x77\ -\xf2\xf4\xb1\x44\x32\xde\xed\x78\xee\xab\x22\x10\x54\x2a\x95\xbe\ -\x09\x80\x01\x58\x10\x38\x00\x02\x55\x55\xf1\xf1\x27\x07\x2b\xf3\ -\x9d\x32\x42\x21\x28\x8a\x82\x64\x32\xb9\xf7\xe2\xa5\xe1\x67\xee\ -\x14\x54\x1a\xed\x3d\xff\x46\x34\x1a\xd1\x4d\x2b\x04\xdb\xb6\x61\ -\x99\x16\x82\x20\xc0\xb1\x7f\x1f\xc7\xf0\x70\x7e\x37\x00\x28\x8c\ -\x31\x30\xc6\x30\x57\xab\x3f\x34\x3a\x7a\xf9\xe7\x91\x68\x04\x8e\ -\xe3\xc0\x73\x1c\xa8\x8a\x82\x89\xc2\x04\xaa\x95\x1a\x9a\x9b\x93\ -\x8d\xbc\xcf\x5f\xc8\x82\x31\x86\x5c\x2e\xdb\x77\x77\x26\x03\xc7\ -\xb1\xe0\xd8\x0e\x34\x4d\x47\xad\x56\x43\x36\x77\x11\x9a\xaa\x63\ -\x79\x66\xd9\x63\x00\xa0\x08\xc9\x01\x12\x1f\x64\x32\xcb\x11\x8d\ -\x47\x60\xdb\x36\x0c\xd3\x00\x63\x0c\x17\x86\x06\x21\x85\x84\xeb\ -\xba\xf5\x42\xa1\xf0\x77\x00\x50\x86\x2e\x64\x7f\xf6\xad\xad\x8f\ -\x6f\xdd\xbc\xe9\xab\xd8\xf6\xe4\xd3\xf0\x1c\x0f\x66\xc8\xc0\x4c\ -\x69\x06\xe3\x63\x05\xc4\x62\x71\x5c\x9b\x9d\x59\x5c\x2c\x16\x01\ -\x00\xca\xd4\xe4\x74\x53\x2c\x16\x43\x32\x99\x44\x3e\x9f\x47\x5b\ -\x7b\x07\xde\x7b\xef\xcf\xb8\x34\x3c\x8c\x58\x3c\x8e\x20\xe0\x7d\ -\x8c\xa9\x53\x2b\x57\xae\x68\x08\x56\xac\x5e\xf1\x32\x63\x0c\xae\ -\xe3\xa2\xc9\xf5\x90\x6a\x69\xc5\x0b\x2f\xbc\x88\xe3\x47\x4f\x20\ -\x91\x88\x22\x10\xbc\xdb\x30\x34\x08\x21\x1b\x02\x92\x12\x3b\x7e\ -\xb5\xc3\xf6\x3c\x0f\x9e\xe7\x21\x1a\x8d\x20\x16\x8d\xe1\x8f\x7b\ -\xde\x41\xad\x56\xfb\x83\xae\xeb\xb8\x6f\xed\x7d\x48\xc4\xe2\x0d\ -\x01\x24\x83\xe7\x3a\xf5\xed\x3f\xde\xce\xba\x37\x77\x6f\x8f\x27\ -\x13\x81\xeb\x79\x58\x75\xcf\xaa\xaa\xae\xeb\x3f\x4d\xb5\xa6\xe0\ -\x79\x1e\x88\xe8\xb6\xf9\x84\x10\x0b\xfe\x20\xa2\x79\xa3\x31\xba\ -\xf5\x95\xaa\xaa\x28\x16\x67\xbe\x76\xfa\xf4\x99\x0f\x6e\x56\x2a\ -\x56\x3c\x1e\x43\x3c\x11\x87\xeb\x3a\x50\x15\x65\x4e\x53\xb5\xb7\ -\x2d\xcb\xda\x6d\xdb\x76\x1e\x00\xa4\x24\x00\x04\x5d\xd7\x3f\x0f\ -\x20\x22\x0d\x40\x2b\x63\xac\x00\x40\xcc\x07\x1f\x1e\xc9\xbf\x74\ -\x61\x30\xfb\xeb\x48\xb8\x09\xae\xeb\xc2\x71\x6c\x84\x0c\x1d\x96\ -\x65\xc1\xb5\x1d\x18\x86\x01\x45\x51\x50\x2e\x97\xe9\xc6\x8d\x1b\ -\x6f\xa4\x52\xa9\xe7\x18\x08\x4c\x51\x3f\x07\x60\xb7\x8b\x68\x54\ -\xc1\x39\xf7\x8e\x1e\x3d\xfe\x69\xdd\xf7\xd7\xc4\xe3\x71\x58\xb6\ -\x09\xc7\xb1\x61\x18\x06\x4c\xd3\x84\x63\xdb\xd0\x35\x1d\x44\x04\ -\xdf\xf7\x31\x39\x35\x85\xfc\xe8\x28\xaa\x95\xb9\xe2\x97\x37\x6f\ -\xba\xdf\xb2\xcc\xcb\x77\x6e\x0f\x01\x20\xc6\x18\x34\x4d\x43\xa9\ -\x74\xed\x91\x8f\x3f\x39\x50\x56\x35\x6d\x4d\x7b\x7b\x3b\x22\x91\ -\x08\x3c\xcf\x85\x69\x99\x70\x1c\x07\xae\x6d\x43\x57\x35\x10\x11\ -\xaa\xd5\x2a\x86\xf3\x23\x18\x1e\xc9\x43\x04\x12\x9e\xe7\x25\x86\ -\x86\xb2\x3f\x04\x00\x0d\x00\xa6\xa6\xa7\xef\xa0\x50\xe8\xf2\xe8\ -\xd8\x87\x53\x53\x57\x1f\x6e\x6f\x6f\x87\x63\x3b\x30\xcc\x10\x42\ -\xa6\x0e\x33\x64\xc0\x32\x2d\x84\x42\x21\xa8\x8a\x02\x41\x84\x72\ -\xb9\x8c\x5c\xee\x22\x2a\x95\x2a\x84\x10\x30\x8c\x10\x16\x2f\x59\ -\x34\xd6\x96\x4a\xed\x59\x00\x14\xaf\xce\x34\x16\x49\xd3\xd6\x15\ -\x26\x27\x07\x38\xe7\xa1\x8e\x25\x8b\x61\x58\x06\x4c\x33\x84\x50\ -\x28\x04\xc3\x34\x61\x19\x06\x74\x4d\x87\xa2\x28\x08\x84\x40\x71\ -\xa6\x88\xa1\xa1\x1c\x38\x0f\x40\x44\x08\x87\xc3\x88\x46\x23\x7d\ -\x86\x11\xea\x9e\x29\x95\xd0\xd2\xdc\xdc\x00\x98\xa6\x89\xc1\xc1\ -\xec\xf3\xbd\xbd\xbd\xbb\x84\x94\x58\xbc\xe8\x2e\x68\xba\x8a\x75\ -\xeb\xee\x47\x93\xd7\x04\x92\x12\x44\x8d\x87\x4f\x61\x0a\x38\xe7\ -\x18\x9f\x2c\x20\x37\x94\x03\x11\x60\x9a\x16\xa2\xd1\x08\xca\x37\ -\xae\xbf\x52\x98\x9c\x78\x7e\xa2\x30\x0e\xcb\xb2\x6e\x03\xde\xff\ -\xcb\x5f\x3f\xdc\xbb\x77\xdf\x37\x18\x63\xa8\xd5\x6a\xf0\xb9\x0f\ -\x9f\x73\x54\x2a\x15\xd4\xeb\x75\x6c\xdc\xf8\x20\x76\xee\x7c\x11\ -\x6b\xd7\xdc\x03\x22\x42\x6e\xf0\x22\x2e\x5d\x1a\x5e\x08\x6c\xd9\ -\x26\x8a\xc5\xe2\x4f\x82\x80\xbf\x1d\x0a\x85\xd0\xd9\xd9\x09\xcb\ -\xb2\x6e\xef\xc1\xbe\x7d\x1f\xb5\xbf\xfb\xce\xbb\xff\x19\x1f\x9f\ -\x68\x99\x87\x08\x21\x20\x84\x80\x94\x12\x04\x42\xcd\xf7\x51\x9b\ -\x9b\xc3\xf7\x7f\xb0\x0d\x5b\xb6\x3c\xd6\xf8\xfd\x47\x9a\x00\x10\ -\x4a\xa5\x6b\x0f\x83\xb1\xfd\x52\x4a\x2c\x5d\xda\x81\x70\x53\x13\ -\xa4\x94\x68\x4e\x24\x1b\x80\x83\x87\x7a\x00\xc6\xd0\x7b\xb8\xf7\ -\xf5\x9e\xc3\x47\x7e\x21\xa4\x80\xaa\xaa\x90\x52\x2e\x40\xa4\x94\ -\x20\x49\x98\xab\xcd\x21\x95\x4e\x61\xf7\x9b\xaf\x41\xd7\x35\xbf\ -\x58\x2c\x7e\xc5\x34\xcd\x4f\x89\x08\xaa\xaa\xc2\x73\x3d\xa4\xd3\ -\x69\xa8\xaa\x0a\xdb\xb2\x6e\x3d\x23\x07\x7a\x1a\x0e\x92\x84\xd9\ -\xd9\x59\x0c\x8f\x8c\x7c\xe7\xec\xd9\xb3\xbf\x9d\x9a\x9c\xba\x2b\ -\x08\x02\xa8\xaa\x0a\xc6\xd8\xc2\x42\x56\xab\x73\xd8\xfa\xed\x2d\ -\x37\x1e\xfd\xfa\x23\x5f\x94\x52\x9e\xe3\x9c\xa3\xb5\xa5\x15\x6d\ -\xe9\xf4\xc2\xac\x00\x40\x51\x94\x5b\x15\x1c\x3c\x3c\xef\x52\x95\ -\x88\x70\xbd\x7c\x53\x08\x21\x50\xab\xd5\x50\x2a\x95\x50\xaf\xfb\ -\xeb\x54\x55\x79\x94\x29\xc8\x44\xc2\x91\xd9\x65\xcb\x96\xfe\x29\ -\x12\x0d\xff\x57\x51\x19\x34\x55\x43\xa6\x73\x39\x74\x5d\x87\x94\ -\x12\x77\x1e\x45\x51\xf0\x7f\x60\x84\x69\x65\x48\xcf\xfa\x14\x00\ -\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ -\x00\x00\x03\x66\ -\x89\ -\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ -\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\ -\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\x74\x77\x61\x72\x65\ -\x00\x41\x64\x6f\x62\x65\x20\x49\x6d\x61\x67\x65\x52\x65\x61\x64\ -\x79\x71\xc9\x65\x3c\x00\x00\x03\x08\x49\x44\x41\x54\x78\xda\x84\ -\x53\x6d\x48\x53\x51\x18\x7e\xce\xfd\xd8\x75\x9b\x8e\xdc\x2c\xdd\ -\x4c\x5d\x4e\xa7\xc9\xe6\xb7\xf6\x61\x61\x11\x14\x52\x16\xf5\xc7\ -\x0a\x0b\xa2\x3f\x41\x51\x41\x61\x7f\x0a\x84\xa2\x9f\xfd\xeb\x67\ -\x7f\xfa\x51\x44\x50\x91\x14\x15\x5a\x14\x41\x25\x6d\x44\x59\x68\ -\x69\xd9\x74\xa6\x6d\xd7\x7d\x38\xb6\xdd\x6d\x77\xa7\x73\x2d\x4d\ -\x84\xe8\x81\x87\xf7\x7d\xef\x7b\xde\xe7\xbe\xe7\x9c\xf7\x10\x30\ -\x48\x84\x20\x4f\xb3\xf8\x8b\xb3\x1b\xe9\xbc\xe5\x38\x14\xb3\x74\ -\x2f\x73\xab\x18\x47\x28\x45\x6f\x36\x0b\xff\xc2\x3a\xde\xc6\xb2\ -\x06\xcd\x61\x24\x4b\x04\xbe\x87\x09\x48\x82\x89\x8a\xb8\x62\xaf\ -\x76\x75\x5a\x4a\xcb\x9d\x31\x85\xae\x9d\x0d\xce\x15\x7c\xf1\xa3\ -\xef\x67\x18\xd0\xc8\xe1\x1f\xf0\xcf\x01\x43\x53\xc4\xf1\x33\x04\ -\x57\x20\x12\x29\xcc\x31\x5b\x84\x4d\x7b\xf6\x18\xb5\x78\xcc\x0f\ -\x07\x23\x34\x0a\xcb\xea\x0a\x19\x4f\x32\xda\x19\xc7\x53\x04\x91\ -\x99\x10\xc4\xde\xd3\xa7\x61\x30\x1a\xa1\xb2\xde\xb5\x98\xe7\xb0\ -\x85\xe5\xc7\xb4\x02\x81\x2e\xa9\x66\xfe\xb9\x86\xd6\xd6\xfd\xee\ -\xba\x3a\xcb\x3b\x8f\x47\x9e\x78\xe7\x8d\xc5\x13\x88\x4a\x3a\x1d\ -\x94\x78\x1c\x82\x28\x22\xae\x6d\x8b\x47\x23\x5b\x7e\x6d\x5e\xa0\ -\xdd\xf9\x77\xe7\xcf\x3e\xd3\x0d\xbd\xa7\x3a\xac\x2e\xa7\x15\x43\ -\x9f\x6d\xd6\xae\x43\xde\xb0\x51\x44\x74\x6c\x78\x18\xf6\x8a\x0a\ -\x68\x96\xc5\x1a\x4a\x16\x6a\x84\xad\xce\xc5\xfa\xae\xc1\x69\x53\ -\x65\xbd\xdb\x8e\x74\x32\x09\xcd\xea\xf2\x4c\xb9\x0e\x5b\x94\x0c\ -\xdc\xba\xe9\x6d\xda\xbe\xa3\xd1\xf3\xe4\xb1\x37\xf7\xb7\x40\xc1\ -\xa2\x40\x26\xbb\x28\xc0\x75\xd5\x29\x23\xc9\xb9\xb9\x8d\x99\x74\ -\x1a\x2a\xe3\xae\xfa\xf4\xc7\xf1\x92\xa2\x60\xce\xc4\x0f\x4b\x85\ -\xb3\x0a\xcf\xfb\x6e\xd2\x57\xdd\x35\x1f\x73\x43\xc9\x47\x33\x25\ -\x26\x4c\x15\xe7\x82\x27\xb5\x07\x41\x09\x87\x7c\x75\x66\xc8\x28\ -\x66\xaa\x4b\x2a\xdd\x4d\xec\x42\x85\xf0\x6c\x20\xf5\x32\x3c\xfa\ -\x4d\x3a\xd1\xe3\xd4\xd7\xb4\x54\xa5\x14\x17\xa6\xdb\xaa\x6d\x85\ -\x5b\xda\x0b\x9e\xe6\x04\x12\xe1\x3c\xc1\x8e\x2c\xfd\xc2\x7f\x6d\ -\xba\x8c\x41\x7d\x07\x1e\x99\x8e\x40\xa5\x24\xc0\x7d\xb8\xb1\x3e\ -\x96\x26\xb6\x57\xaf\x07\xfc\x74\x77\x77\x45\xc1\x6a\x87\x79\x2a\ -\x91\xc0\xd9\x8e\xa3\xb8\x3d\xe5\x41\xe9\xaa\x62\x93\xcb\x5c\x5e\ -\x6b\xa0\xba\x35\xdf\x02\x93\xe2\x92\x39\xa0\xcd\xfd\xa6\xc3\x3b\ -\x83\xf2\x2c\x69\x6c\x6e\x41\x24\x1a\x13\xef\x8f\xb4\xbe\x1f\xf7\ -\x49\x93\x49\x76\x26\xb2\x2c\x43\xb3\x1a\xd4\x54\x46\xaa\x36\x97\ -\xb9\x69\x54\x69\x23\x7c\x77\xdf\x0a\x70\xe2\x7e\x83\x24\xd4\x1c\ -\xeb\x74\xef\x5b\x19\x19\x2a\xb6\x4b\x32\xc6\x15\x0b\x82\xf9\x95\ -\xa1\xab\x0f\xfb\x3d\x49\xce\x17\x6b\x19\xf6\x0e\x0c\x6e\xf0\x6f\ -\xa3\x69\x55\x0f\x45\x35\xd0\x74\x36\x07\xa3\xd1\x27\x84\x3f\x70\ -\xe7\x4c\xe7\xfa\xf2\xee\xa6\x2a\xeb\x5a\x4b\x7e\x9e\xe4\xf3\x4d\ -\xe3\xd2\xde\x52\x9c\xbf\xeb\x43\x59\x99\x15\x72\x28\x9a\x7a\xfb\ -\xe9\xfb\x68\x5f\xff\xeb\x7b\xea\x83\x93\xd7\x97\x0d\x9e\xcc\x41\ -\x89\x36\xd7\xda\xcd\xf5\xd9\x4c\x76\xfe\x2d\x2d\x6f\x97\xaa\xd0\ -\xd5\x39\xac\x35\x90\x4c\xe5\xfc\xe6\x9e\x11\xed\x41\x2d\x61\x90\ -\xf0\xf5\x87\x2e\xc0\xda\xd0\x4e\x79\x29\x41\x05\x7d\x0c\x82\x3e\ -\xde\x36\x7d\xf5\xcd\xcb\xa2\xe3\xeb\x48\x26\x69\x20\x99\x84\x91\ -\xa8\x8a\x1e\x3f\xbc\x2f\xe8\xec\xe8\x45\x1a\x99\x04\x8d\x4c\x2c\ -\xb6\x40\xfe\x0c\x85\x05\xff\x87\xac\xfd\x71\xf9\xc7\x5f\x02\x0c\ -\x00\x00\x31\x44\x70\x94\xe4\x6d\xa8\x00\x00\x00\x00\x49\x45\x4e\ -\x44\xae\x42\x60\x82\ -\x00\x00\x02\xcc\ +\x00\x00\x03\x34\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ \x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x01\x68\xf4\xcf\xf7\ \x00\x00\x00\x06\x62\x4b\x47\x44\x00\xff\x00\xff\x00\xff\xa0\xbd\ \xa7\x93\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0b\x13\x00\x00\ -\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x02\x6c\x49\x44\x41\x54\x38\ -\xcb\x4d\xd2\xbd\x6b\xdd\x65\x14\x07\xf0\xcf\xaf\xb9\x49\x9a\x10\ -\x69\x08\x06\x6a\x2c\x82\xf4\x25\x2e\xed\x50\x87\x4e\x85\x2e\x42\ -\xb2\x34\xe0\xe4\x58\x10\xea\x58\x69\x57\x47\x57\x83\xd2\x49\xc1\ -\xad\xfe\x01\xba\x28\xb8\xa8\x93\x83\xa0\x4b\x41\x89\x10\x1b\x43\ -\x20\x37\x37\xf7\xe6\xbe\xfe\xee\xef\x2d\xc7\xe1\xb9\x6a\x1f\x38\ -\x1c\x9e\xf3\x7c\xbf\xe7\x7c\x9f\x73\x4e\x16\xb7\x6e\x71\xf5\x6a\ -\x2d\xae\x5f\x2f\x82\x10\x0f\x1e\x6c\xc6\x8d\x1b\xc4\xc5\x8b\xf1\ -\x17\x21\xee\xdf\xbf\x19\x77\xee\x44\xac\xad\x45\xcc\xcf\x47\x36\ -\xc4\x11\x91\xe3\x67\x64\xb1\xb3\xc3\xa5\x4b\xbf\xd8\xdf\xff\xd1\ -\xf3\xe7\x4f\xc4\xce\x4e\xc4\x95\x2b\x11\xab\xab\x31\xa0\x16\x1b\ -\x1b\x11\x44\x45\xfc\x40\x64\x07\xc4\x18\x2f\xb0\xc7\x6e\x16\xdb\ -\xdb\xac\xaf\x7f\xab\x69\xb6\x74\x3a\x9c\x9d\x31\x1e\x27\xdf\xed\ -\x2e\xb6\x8c\xc7\x7b\x8e\x8f\xaf\x39\x3c\xe4\xf4\x94\xf3\x73\xd0\ -\x8f\xd0\xa6\x10\xcb\xcb\x4f\x83\x28\x67\x56\x10\x6d\xe2\x27\xe2\ -\x19\x91\x75\x92\x6e\x6d\x86\x7d\x56\x06\xe8\xa2\xe6\x83\xd7\xf9\ -\x22\x8b\x7b\xf7\xd8\xd8\x60\x71\xf1\x13\x79\xfe\xc8\xd9\xd9\x6f\ -\xda\xed\xf7\xb4\xdb\x7f\xea\xf5\xb4\x5c\xbe\xbc\x60\x7e\xbe\xd0\ -\xef\xd3\xe9\x30\x1a\xbd\x6d\x30\xd8\x33\x99\x7c\xa7\x28\xb6\x5b\ -\xca\x72\xa2\xdb\xe5\xe0\x20\x89\xac\x6b\xea\x5a\x33\x1c\x6e\x9d\ -\xb1\xd9\x72\x7c\x3c\xa7\xdd\xe6\xf0\x90\xe9\x14\x54\x11\x4e\xd0\ -\xe1\xab\x96\xa3\x23\xfa\xfd\xf4\x18\x21\x90\xe3\x24\x89\x7f\x23\ -\x8b\x56\x2b\x9a\xba\x56\x63\x0e\x25\xfe\xc6\xef\x18\xf0\x59\xd6\ -\xe6\xd3\x21\x8f\x4a\x34\x29\xe8\x45\xfa\xb6\x55\xb2\xd6\x84\x0f\ -\x8f\xd9\xef\x26\xa0\x5e\x02\x8d\x96\x79\xe5\x35\x64\x71\xf7\x2e\ -\x6b\x6b\xac\xac\xb0\xb0\xf0\x58\x96\x7d\xac\xae\x97\x14\x45\xd2\ -\x35\x9d\x52\x14\xe4\x39\x93\x49\x6e\x32\xf9\xc8\x64\xb2\x2b\xcf\ -\x29\xcb\xd9\x42\x2c\x2d\x7d\xee\xc2\x85\x87\xaa\x2a\x01\x87\x43\ -\x46\xa3\x44\x2e\x4b\x9a\x26\x59\x59\xa6\x58\x9e\x8b\xe9\x74\xb7\ -\xe2\x49\x4b\x51\x3c\x55\x96\x0f\x4d\xa7\x89\xd8\xeb\xa5\x4d\xc8\ -\x73\xaa\x8a\x08\x20\xcb\xa8\x6b\x65\x84\x1c\x13\x1e\x17\xcc\x65\ -\x71\xfb\x76\xa1\xae\x17\xe4\x79\x5a\xa3\xe1\x30\x91\x9b\xe6\x7f\ -\x32\xff\xb5\x77\x34\x6b\xd4\x20\x25\x39\x69\x39\x3a\x3a\x50\x55\ -\xd7\x54\x55\xaa\x58\x96\xa2\x69\xbc\x7c\xce\x67\xed\x1f\xa6\xe1\ -\xe9\xe2\x0c\x05\x07\x59\xc3\xcd\x29\xbf\x56\xcc\xd5\xb3\x4a\x90\ -\xcd\x7c\x83\xe9\x4b\xe4\x53\xf4\x53\xc2\x66\x81\xb7\xb2\x6e\x92\ -\xb5\x30\xe6\xeb\x9c\xad\x7f\xe7\xd9\xa0\x4a\x55\xe4\x33\xc9\xa3\ -\xd9\x1d\xdf\x2c\xf3\xee\xab\x34\x59\x0f\xe3\x19\xa0\x9f\xfc\x9b\ -\x23\xde\x1f\xf1\x4e\xce\x66\x91\x12\xfd\xd1\xf0\xfd\x1c\x5f\x2e\ -\xb1\x7f\x09\xeb\x33\xfb\x07\x6a\x4f\x76\xe7\x35\x05\x41\x4b\x00\ -\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ -\x00\x00\x02\xf0\ -\x89\ -\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ -\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\ -\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\x74\x77\x61\x72\x65\ -\x00\x41\x64\x6f\x62\x65\x20\x49\x6d\x61\x67\x65\x52\x65\x61\x64\ -\x79\x71\xc9\x65\x3c\x00\x00\x02\x92\x49\x44\x41\x54\x78\xda\x84\ -\x53\x5f\x48\x53\x61\x14\x3f\xf7\xee\x6e\x22\x0b\x4c\x6b\xac\x60\ -\x6a\xb5\x39\xdd\x06\x36\xd9\x70\x6b\xb8\x31\xd4\xb2\x35\x82\x3d\ -\x4c\x50\x50\x08\x1f\x24\x84\x24\x56\xea\xc3\xdc\x2a\x63\x36\x1d\ -\x4d\xa1\xb0\xf0\xc1\x17\x1f\x7c\xb0\x3f\x0f\x51\x96\x12\xe8\xa6\ -\x84\xa6\xeb\x45\xa9\x84\x22\x25\x08\xb7\x97\xec\x45\xe6\xee\xdd\ -\xed\x7c\x97\xb5\x36\x1a\x74\xe0\xc7\xe1\xfb\x9d\xf3\xfb\x9d\xf3\ -\xdd\x8f\x4b\x41\x4e\xd0\xc5\xc5\x20\xa9\xa8\x80\xc3\xdd\x5d\x48\ -\x1f\x1c\x40\x75\x43\x03\x68\x6d\x36\xa0\x45\x22\xa0\x69\x5a\x85\ -\x2d\x8d\x90\x1f\x3f\x18\x28\x10\x4a\xa3\x11\xaa\x4d\x26\x41\x98\ -\x89\xaa\x74\x3a\xdd\x38\x3b\x34\xf4\xf8\x0f\x41\x21\xdc\x7e\xff\ -\xd5\x3c\x83\x53\x7a\xbd\x20\x16\x31\x79\x74\x55\x9a\xe3\x9a\x66\ -\x03\x81\x47\xd1\xf5\x75\xf8\xb0\xb5\x05\x75\x3a\x1d\x58\xb1\x0f\ -\x79\x4a\xe8\x2c\xaf\xab\x83\xd3\x48\x30\xcc\x3f\x0b\x55\x71\x1c\ -\xd7\xfc\x34\x18\x9c\xc0\x0c\x89\xfd\x7d\x28\x2d\x2b\xa3\x30\xf3\ -\xa4\xc8\x11\x03\x53\x47\x07\x88\xc4\xe2\x42\x37\x51\xe3\x84\xf3\ -\xcf\x42\xa1\x87\xc9\x64\x12\x44\x78\x1d\x8d\x52\x09\xdf\xe3\x71\ -\xbe\x5c\x2e\x17\x1a\xb0\x4e\xd3\x50\x38\xd4\x2c\xc7\x5d\x78\x82\ -\xe2\x58\x2c\x06\x57\x70\xc8\xd6\xe6\x26\x9c\x51\x28\xc0\x6e\x30\ -\x80\xba\xb2\x12\x2e\x79\x3c\xd7\x70\x83\x85\x42\x06\xd5\x1c\xcb\ -\xb6\x3c\x0f\x87\x1f\xbc\x5f\x5b\x83\xbb\x7e\x3f\x1c\xe0\x8b\xdc\ -\x1a\x1c\x24\x2b\x0b\x1f\xd6\xd1\xdb\xdb\x8b\xd3\x17\xf0\x1e\xdb\ -\x4c\x01\xf1\xc5\x17\x13\x13\xe3\xef\x56\x56\xe0\x8e\xd7\x9b\x2d\ -\x04\x46\x47\x41\x52\x54\x04\x2d\x3d\x3d\xd7\x29\x8a\x9a\x47\xa3\ -\xcf\x84\xcf\x35\xa8\x61\x59\xd6\xf1\x6a\x72\x32\xbc\xbc\xb4\x04\ -\xbe\xfe\xfe\x6c\x61\x64\x6c\x0c\x8c\xf5\xf5\xd0\xdc\xdd\xed\x41\ -\xf1\x1b\x51\x46\x9c\x6b\xa0\x21\xe2\xd7\x53\x53\xf7\x23\x8b\x8b\ -\xe0\xef\xeb\xcb\x8a\xef\x85\xc3\x60\xb6\x58\xa0\xb1\xab\xeb\x06\ -\x8a\xe7\x50\xfc\x29\x77\x65\x62\xa0\xe1\x52\x29\xe7\xfc\xf4\x74\ -\x28\x8a\xe2\x00\xae\x2d\x91\x48\x84\xe2\xed\x60\x10\x2c\x56\x2b\ -\xd8\x3b\x3b\xfb\x80\x88\x19\x26\x2b\xfe\x8a\xdf\xe7\xcb\xea\x2a\ -\x30\x38\xf9\xf2\xdb\x99\x99\x91\xbd\x78\x1c\xc6\x87\x87\x41\x2a\ -\x95\x0a\x0d\x37\x7d\x3e\x41\x6c\x6b\x6f\x1f\xc0\xc9\x2f\x71\xf2\ -\x47\xc2\xef\xe0\xab\xec\x6c\x6c\xfc\x5d\x41\xdf\xda\xaa\x3d\xeb\ -\x76\x0f\xfc\xe4\x79\x7e\x2e\x12\xe1\x5d\x2e\x17\x3f\x1f\x8d\xf2\ -\xbf\xf0\x4c\x78\x52\x37\xb4\xb5\x81\xa2\xb6\xb6\xf0\x83\x9f\xd0\ -\x6a\xa1\xc6\xe9\xd4\xa9\x1d\x0e\x2f\x31\xd9\xde\xdb\xe3\xf7\x31\ -\x93\x33\xe1\x49\xfd\x7f\x41\xfe\x98\x92\x12\x95\xaa\x49\x6e\x36\ -\x0f\x11\x13\x92\x8f\xaa\x54\x76\xe4\x8f\x21\x4a\x49\x1d\x71\x04\ -\x51\x8c\x28\x42\x88\x33\x3a\x8a\xca\x1c\x4e\x22\x8e\x4b\x64\x32\ -\x85\x58\x26\x3b\x97\x4a\x24\x96\x0f\x13\x89\x6f\xc8\xa5\x10\x6c\ -\x26\x13\x1c\xe6\x70\x04\xdc\x6f\x01\x06\x00\x2d\x06\x04\x62\x7f\ -\xe8\x51\x71\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ -\x00\x00\x02\x75\ -\x89\ -\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ -\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\ -\x00\x00\x00\x19\x74\x45\x58\x74\x53\x6f\x66\x74\x77\x61\x72\x65\ -\x00\x41\x64\x6f\x62\x65\x20\x49\x6d\x61\x67\x65\x52\x65\x61\x64\ -\x79\x71\xc9\x65\x3c\x00\x00\x02\x17\x49\x44\x41\x54\x78\xda\xa4\ -\x93\x4f\x6b\x13\x51\x14\xc5\xcf\x9b\x37\x93\x49\xc6\xa9\x25\xa3\ -\x15\x63\x16\x2e\x02\x06\xad\x0b\x45\xc1\x95\x5f\xc0\x3f\x0b\x21\ -\x1b\xa5\x20\xb8\x31\xea\x7c\x82\xac\xf4\x2b\x54\x5b\x23\x88\x1b\ -\x4b\xb3\x70\xe1\x42\xb7\x82\x20\xe8\x42\xbb\x70\xa1\x62\x36\xa2\ -\xa0\x51\xda\x1a\xc2\x4c\xa6\x26\x93\xcc\x78\xef\x75\x5a\xb4\x2a\ -\x54\x7c\x70\xb8\x2f\x93\x7b\x7e\xf7\xbc\x37\x89\xaa\xd5\x6a\x50\ -\x4a\x9d\x06\xb0\x07\xff\xb6\x3e\xa5\x69\xfa\xc0\x4c\x92\x84\x3f\ -\x94\x9b\xcd\xe6\xcd\x38\x8e\xb7\xe4\xb4\x2c\x0b\xf5\x7a\xfd\x12\ -\xef\xcd\xd1\x68\xc4\xd5\x18\x0c\x06\xf0\x7d\x1f\x0c\x64\x11\x5d\ -\xea\x78\x3c\x46\x18\xf6\xa9\xa6\x62\xf4\x3c\x0f\xf3\xf3\xd7\x41\ -\x3e\xe3\x67\x80\xe2\xca\x86\x6a\xb5\x0a\x4e\xf2\xed\x68\x05\xa3\ -\xc7\x2f\xb1\xb2\xf2\x95\x9e\x6b\x32\xdb\xb0\xed\x3c\xa2\x28\x60\ -\x33\x4b\x09\x20\x8b\x6d\xf0\x43\x9e\xc6\x49\x58\x69\x79\x07\x56\ -\x57\xbb\x64\x88\x91\xcf\x6f\x13\xb3\x65\xe5\xa9\x27\x16\x00\xf9\ -\x8c\x0d\x23\x49\x33\x48\x00\x34\x39\x8a\x22\xa8\xbd\xbb\x08\x94\ -\x60\xf2\x60\x05\xe5\xd9\x3a\x26\x4f\x1c\x13\x90\x69\xda\x92\x90\ -\x3d\xec\x35\x86\xc3\x21\x48\x3f\x40\xa5\x22\x9d\x37\x84\x73\xed\ -\xbc\x5c\xd6\xf6\xe9\x0a\x3c\xff\x14\x7a\x8d\x16\x01\x8b\xa8\x35\ -\xaf\x12\xc0\x94\x04\xec\x61\xef\xc6\x11\xb8\x26\xef\xbf\xa0\xdf\ -\x5d\x43\xf7\xe1\x53\xb8\x07\xf6\xa1\x78\xf9\x24\xfa\xb7\x1f\xc1\ -\x75\x8b\x48\x5b\x4b\xb8\x77\xf7\x19\xbf\x72\x49\xb0\x7e\x04\x93\ -\x29\xb4\x24\x8e\xe3\x38\xe8\xf5\x7a\x30\x0c\x0b\xfa\xed\x07\x84\ -\xfe\x2c\x0a\x85\x09\x0c\x0c\x2d\x46\x5e\xb6\xad\xd7\x13\x68\x01\ -\xb4\xdb\x6d\x94\x4a\xa5\x1c\x37\x34\x1a\x8d\x2d\xfd\x0e\xb8\x37\ -\x08\x82\x5c\xa7\xd3\x01\x63\x3d\xd7\x75\x67\xb4\xd6\xbb\x37\x37\ -\xd2\xa4\xb2\x4c\x31\xcd\x8f\x9b\xbf\xa3\x0b\xff\x4c\xf7\xb5\xc0\ -\x80\x02\x69\x82\xfb\x7e\xe9\x98\xce\x01\xaf\x86\x7e\xb6\xbf\x41\ -\xfb\xdf\xf8\xa4\x40\xfd\x35\xe7\xe2\xd4\x2d\xbc\x89\x8f\xc8\x7e\ -\xbf\xb5\x84\x73\xcb\x17\xff\xd4\xc6\x53\x77\x92\x2a\xa4\x43\xa4\ -\xc3\xa4\xaa\x24\x5a\x0c\x71\xe6\xce\x59\x01\xdc\xbf\xd0\xe2\xf2\ -\x82\x93\x93\xde\x65\xfb\xe7\xa4\xd7\x9c\xc0\xca\x8e\xe1\x66\x72\ -\xf8\xad\xe0\x8a\x33\x83\x29\x75\x5c\xc6\x2c\xa7\x4f\x30\x17\x2d\ -\x64\x43\xd7\x38\x7a\xa6\x48\xf1\x9f\xe6\x7f\xd6\x77\x01\x06\x00\ -\xf9\x1f\x11\xa0\x42\x25\x9c\x34\x00\x00\x00\x00\x49\x45\x4e\x44\ -\xae\x42\x60\x82\ +\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x02\xd4\x49\x44\x41\x54\x38\ +\xcb\x55\x92\x4f\x68\x5d\x65\x10\xc5\x7f\xdf\xbb\x37\xaf\x49\x48\ +\xd5\x04\x23\x22\x22\x88\x85\x08\x12\x05\x0b\xa2\x0b\xc1\x8d\x90\ +\x6c\x14\x04\xa1\xee\x94\x42\x15\x14\x22\xed\x56\x77\x6e\x0d\x82\ +\x2b\x05\x97\xba\x15\xdd\x58\x10\x0a\x82\xab\x5a\x50\x2b\x41\x24\ +\xb1\x31\x31\x79\x6d\xfe\xbc\x97\xfb\xde\xfd\xf7\xdd\xef\x7e\x33\ +\xe3\xe2\x3e\x0a\xce\x76\xe6\x9c\x39\x67\xe6\xb8\x67\x77\xde\xe4\ +\xa9\xfe\xe3\xb1\x57\xab\x6f\xbe\x9d\xdc\x48\x92\xb7\x0f\x3e\x5e\ +\xa9\xde\x7b\x68\xe8\x66\xb7\x5e\x30\x7f\x98\xe3\x5e\xdb\xdb\x58\ +\x3d\x8a\xc3\xdb\xdb\x61\x9f\x5c\x4b\x1c\x37\x57\xe1\xb8\x35\x1a\ +\x85\xdf\x2b\xdc\xeb\x7b\x1b\x3c\x98\x9c\xbf\xb5\x1b\x0e\x7f\xda\ +\x6a\xfe\xbe\x96\x02\x76\xa3\xbc\x49\xa1\xd5\xc5\x6c\x32\xde\x48\ +\x7f\xa9\xb7\x18\xc4\x13\x10\x83\x3f\xab\x24\x1d\x1c\x0c\xa0\x56\ +\x18\x04\xd8\x6b\x36\xdd\xfa\x3f\xef\xb3\x9c\x2e\xfe\x20\x26\x6b\ +\xa7\x92\x91\x49\x4e\xa9\x35\x99\xe6\x8c\x64\x7c\x2e\x2d\xb5\xde\ +\x3e\xf2\xc3\x0b\x07\xf1\x88\xa1\x64\xa8\x19\x00\x56\x44\x18\xc6\ +\x26\xbd\xe5\xb7\xae\x57\xea\x3f\x20\x76\x0d\x0c\x28\x04\xee\x34\ +\x70\x37\xe0\xf8\xf9\x19\x38\x89\x30\x8c\x39\x85\x2c\x50\x08\x8c\ +\x05\xc4\xde\xe5\x91\x99\x2f\xdd\x2b\xbb\x97\x79\x2c\x5d\xe6\x9c\ +\xeb\x7f\x5a\x5b\xb3\x91\x49\xfe\xdb\x71\x1c\x5d\x3a\x96\xd1\xce\ +\x99\x4c\x48\x1f\x4d\x1f\xee\xcf\xb8\xb4\x19\x6b\xc1\x69\xcc\x28\ +\xb4\xba\x38\xd1\x62\xbb\x52\x7f\xbd\xb1\xb0\x9e\x06\x6b\xab\x91\ +\x8c\xd9\x6f\xef\x31\x94\x8c\x68\x42\x34\x21\x8f\xc5\x1a\x13\x59\ +\x49\x8f\xe2\x30\x39\x8e\x23\x0e\xe2\x11\x5e\x43\xa7\x33\x2a\x8c\ +\x22\x64\xf2\x75\x3a\x88\x27\x8c\xa5\xc0\x6b\xc0\xb0\xce\x85\x57\ +\x38\x8b\x70\x1c\x9f\x48\xff\x6d\xef\x11\x25\x42\x04\x12\xa0\x35\ +\x38\x8d\x70\x18\xa0\xd4\x6f\x12\x7d\x6b\x69\x91\x91\xbc\x48\x26\ +\x1d\xed\xdd\x00\xbb\x4d\x67\xbd\xdf\x7b\x29\xa5\xd6\x0f\x19\xb6\ +\xbb\x8c\xe5\x33\x4a\x85\x89\x40\x21\x05\xb3\xbd\xf3\x2c\xa7\xb8\ +\x97\xef\xbc\xc3\x52\xf2\x00\x0b\xbd\x79\xfa\x6e\xe6\xaa\x73\xee\ +\x93\x68\x32\xd7\x58\xc0\x6b\x83\xb7\x40\x63\x81\x5a\x1b\x2a\xf3\ +\x75\xa5\xfe\xa3\x4a\xeb\xcd\xda\x1a\x82\xb5\x5d\x20\xe6\x7a\xb3\ +\x5f\xf4\x70\x57\x5a\x8b\xd4\xd6\x90\x6b\x49\xa1\x35\x5e\xbb\x21\ +\x41\x11\x13\x82\xb5\xf8\x29\x99\xd7\x66\x93\x68\xd7\xd2\xc6\xda\ +\xcf\x83\xc4\x2b\x7e\x0a\x3c\x93\x9c\x4c\x26\xd4\xd6\xd0\x5a\xec\ +\x2e\x03\x38\x1c\xd1\x04\x6b\x15\x1a\x85\x5a\xaf\x12\x2c\x71\xcf\ +\xef\x5c\x6a\x22\xd2\xaf\xd5\x53\x6a\x4d\xae\x15\xb5\x79\xc4\xf4\ +\x3e\xf8\x7e\x48\x1a\x85\x4a\xbb\xb0\x14\x0a\x5e\x4f\xd2\x41\x3c\ +\xd9\x6f\xad\xbd\xd0\x5a\xa4\x25\x76\x92\x55\xf9\x5f\x99\x75\xe7\ +\x2f\xa7\xff\x19\x0b\xe4\x02\xc1\xf6\x1d\xb7\x9f\x5b\x25\xe8\xaf\ +\x44\x4b\x88\xd3\x47\x76\x9a\xbb\xd2\xe9\xe6\x52\x21\x8b\x90\x4d\ +\xc1\xad\x09\x33\xee\xe9\x94\x42\xfe\xa0\xd2\x79\x6a\xfd\x0e\xaf\ +\x6b\xb4\x06\x6a\x20\x40\x34\x08\xd6\x11\x14\xd2\xc9\x0f\x06\xf0\ +\x3d\x73\xbd\x37\x98\xef\x49\x8a\x03\x7a\x04\xcc\xd6\x09\x06\xa5\ +\x3c\x49\xa5\x97\xa9\xf4\x55\xbc\xae\xd0\x1a\xb4\xf6\x17\x6a\x3f\ +\xd2\x73\x5f\x31\xeb\x76\x59\x48\x60\x29\x85\xc5\x94\xff\x00\xe1\ +\x78\x1f\x4c\x73\x1c\xbc\x8b\x00\x00\x00\x00\x49\x45\x4e\x44\xae\ +\x42\x60\x82\ \x00\x00\x07\x6a\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ @@ -1016,60 +1182,356 @@ qt_resource_data = "\ \xdf\xb5\x30\xe2\x5d\xcd\x84\xfe\x0a\xe5\xc0\xa4\x2e\xcf\x12\x55\ \x4f\x61\x1b\xfc\x1f\x0b\x03\xc8\x05\x59\x65\x3b\x42\x00\x00\x00\ \x00\x49\x45\x4e\x44\xae\x42\x60\x82\ -\x00\x00\x03\x34\ +\x00\x00\x00\xc5\ +\x89\ +\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ +\x00\x00\x90\x00\x00\x00\x90\x08\x06\x00\x00\x00\xe7\x46\xe2\xb8\ +\x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\ +\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0e\xc4\x00\x00\x0e\xc4\ +\x01\x95\x2b\x0e\x1b\x00\x00\x00\x67\x49\x44\x41\x54\x78\x9c\xed\ +\xc1\x31\x01\x00\x00\x00\xc2\xa0\xf5\x4f\xed\x69\x09\xa0\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\ +\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xe0\x06\ +\x44\x9f\x00\x01\xc3\xcd\x96\xea\x00\x00\x00\x00\x49\x45\x4e\x44\ +\xae\x42\x60\x82\ +\x00\x00\x02\x7a\ +\x89\ +\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ +\x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x00\x1f\xf3\xff\x61\ +\x00\x00\x00\x06\x62\x4b\x47\x44\x00\xff\x00\xff\x00\xff\xa0\xbd\ +\xa7\x93\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0b\x13\x00\x00\ +\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x00\x07\x74\x49\x4d\x45\x07\ +\xdd\x06\x0d\x08\x1d\x33\x51\xf1\xd4\x9e\x00\x00\x02\x07\x49\x44\ +\x41\x54\x38\xcb\x65\x91\x3d\x6b\x14\x41\x18\xc7\x7f\x73\xb7\x2c\ +\x6c\x0c\xb9\x20\xa6\x0d\x58\x45\x90\x88\x1f\x40\xb1\x12\x2e\x8d\ +\x82\x5f\x41\x08\x58\x05\xb4\xb5\x0c\xd8\x09\xa2\x20\x08\x7e\x88\ +\x58\x05\xac\x04\xfb\x08\x56\x42\x40\x48\x9d\xdc\xe5\xf6\x66\xe7\ +\x75\x67\xc7\xe2\x19\x72\x07\x0e\xfc\x18\x06\xe6\xff\x32\xcf\xa8\ +\x9c\x1f\x00\xb7\x81\x09\x70\x0b\xa8\xf7\x40\x1d\x42\x7a\x02\xe1\ +\x21\x78\xc0\xfe\x82\xee\x07\x74\x9f\x41\x9f\x83\x41\xf0\xa8\x9c\ +\x1f\x17\x83\x4d\xa0\x7e\x0d\xea\x18\xfa\x46\x84\xae\xe0\x01\x0b\ +\x18\x0b\xe6\x2d\x98\xf7\x72\x0e\xa8\x9c\x0f\x80\x49\x0d\xf5\x09\ +\xa8\x29\xf4\xe5\x72\x57\x76\x0f\x44\x20\xac\x19\x9a\x53\x70\xcf\ +\x21\x84\x11\xd4\x00\x1f\xa1\x9f\x4a\xad\x05\x70\x05\x5c\x96\x7d\ +\x06\x5c\x03\xcb\x62\xda\x01\x66\x9a\xb3\x79\x17\x42\x8f\xca\xf9\ +\xd9\x3e\x54\x67\x90\xc6\x92\xb8\x28\xe8\x92\x9e\x80\x5c\x48\x80\ +\x23\xa5\x88\x31\xa4\x10\xb8\x5f\x41\x38\x84\x38\x96\x6a\x4b\x60\ +\x5e\x12\x6d\xa9\x9e\x91\xa5\x80\x9e\x10\x32\xd6\x82\x31\x8c\xbd\ +\xe7\x55\x05\x66\x2a\xce\xb6\x18\x2c\xcb\x84\x03\x30\xb0\xbe\x62\ +\x14\x71\xd7\x09\xd6\xf2\xa8\x02\xbd\xfb\xff\xe0\x62\x11\xe7\x1b\ +\x71\xce\x10\x23\x78\x0f\xc6\xc0\x72\x09\xc6\xb0\x5b\x49\x62\xcf\ +\xea\xdb\xe2\xda\xbb\x57\xe2\x94\xa0\xef\xb9\x69\x50\x0c\x18\xc1\ +\xf2\x02\xda\x32\x34\x49\xcf\x39\x93\x33\x37\x0c\x83\xa4\x5b\x0b\ +\x5a\x43\xdb\x0a\x5d\xc7\xc5\x08\xda\x53\x99\x7a\x4b\x4a\x96\x18\ +\x13\x21\x48\x5a\x4a\xab\xda\x5a\xc3\xf5\x35\xcc\x66\x42\xdb\x82\ +\xb5\xfc\x54\x29\xb1\xef\x1c\x67\x31\x32\xee\x7b\x49\x04\x50\x4a\ +\xf6\x94\xc0\x39\xa9\x7c\x79\x09\x57\x57\xb0\x58\x40\x08\xa4\xba\ +\xe6\x5e\x65\x0c\xbf\xad\xe5\x93\x73\x1c\xc5\x28\xc9\xc3\xb0\x12\ +\xf7\xbd\xbc\xb5\x6d\x61\x3e\x17\xb1\xf7\x30\x1a\xf1\xa1\x69\x38\ +\x57\xb3\x19\x68\x4d\xdd\x75\x9c\x58\xcb\x34\x04\x11\xae\xd7\xb7\ +\x56\x0c\xb4\x96\x33\xf0\x6d\x63\x83\x17\x77\xee\x90\xaa\x61\x80\ +\x61\x20\xc4\xc8\x81\x73\x1c\x19\xc3\xb1\x73\x6c\x7a\x0f\x21\x48\ +\x7d\x6b\x85\x18\xd1\x4a\xf1\xa6\x69\xf8\xb2\xb5\x05\xdb\xdb\xa0\ +\xe6\x73\xf9\x96\xb6\x95\x7a\x6d\xcb\x5d\xad\x79\xa9\x35\x4f\xad\ +\x65\xcf\x7b\x88\x91\x3f\x29\xf1\x7d\x3c\xe6\x6b\xd3\xf0\x77\x32\ +\x81\x9d\x1d\xe1\x1f\x3c\x20\x6c\x94\x65\x65\x77\x27\x00\x00\x00\ +\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ +\x00\x00\x06\xc9\ +\x89\ +\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ +\x00\x00\x90\x00\x00\x00\x90\x08\x02\x00\x00\x00\x68\x24\x75\xef\ +\x00\x00\x00\x03\x73\x42\x49\x54\x08\x08\x08\xdb\xe1\x4f\xe0\x00\ +\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0e\xc4\x00\x00\x0e\xc4\x01\ +\x95\x2b\x0e\x1b\x00\x00\x06\x6c\x49\x44\x41\x54\x78\x9c\xed\x9d\ +\x3b\x7a\xd3\x40\x14\x46\xc7\x7c\x2e\x48\x9f\x02\x0a\x16\x91\x2d\ +\x50\xb2\x04\x52\x7a\x5b\xa4\xcc\x16\x52\x66\x09\x64\x11\x14\x2e\ +\x48\xef\x52\x14\x0a\xf2\x58\x96\x34\x8f\xfb\xfa\xe7\x7a\x4e\x05\ +\xc4\x1e\x8b\x39\x3e\x1e\x7b\x90\xc5\xee\xd7\xdf\x2f\x21\x84\xc3\ +\xfd\x31\x60\xf0\x78\xf7\x66\x7d\x08\x73\x9e\x4f\x0f\xd6\x87\xf0\ +\xc1\xd3\xfb\xd7\xfd\xf4\xab\x80\xa1\x6d\x9c\x1d\x40\x6d\xb6\x8c\ +\x82\x42\x08\xbb\x61\x18\xa6\xdf\x8c\x20\x68\x1b\x01\xd1\x66\x5b\ +\xd8\xcc\xce\x7e\xed\x16\x08\xda\x6e\xbc\xb6\x99\xaa\x10\xc2\xe1\ +\xfe\xb8\x1b\x86\x61\xf1\x67\xd3\x2d\xc4\x8f\x2b\x0f\x43\x6d\xfa\ +\x85\x6d\xe8\x58\x28\xec\xfa\x9e\x08\xda\x6e\xa4\xb6\x35\x55\xe1\ +\xbf\x85\x8f\xc2\xb6\x6f\x1a\xdf\x01\x01\x65\x6d\x3a\x85\x65\xce\ +\x7f\xa2\xb0\xeb\x11\x11\xb4\x39\xab\x2d\xa9\x2a\x44\xd3\x7e\x2e\ +\x2c\xf3\x9e\xb3\xfb\x9b\xa3\xa0\x4d\xae\xb0\x8a\x09\x2f\x28\xec\ +\xfa\x91\x10\xb4\x35\x5a\x5b\xbe\xaa\x70\x39\xcf\x17\x85\x95\x0e\ +\x74\x3d\x9c\x2d\x42\xda\x78\x0b\x23\xce\x70\x65\x61\xd7\x47\x80\ +\xa0\x0d\xbc\xb6\x0a\x55\xe1\x6a\x62\x3f\x6d\xff\xb8\xe8\x68\xea\ +\x0e\x88\x1d\x9c\xad\xbf\x09\xc6\xc9\x61\x28\x2c\xc6\xbc\xb6\x38\ +\xaf\xe7\xd3\x83\x79\x6d\x44\x4f\xd7\x33\xb9\x20\xec\x70\x7f\x24\ +\x3e\x8c\x89\xb6\x45\x37\x86\x2f\x92\x42\xaf\x37\xcc\x85\xc5\xa8\ +\x69\x4b\xfa\x50\xd6\xc6\xa5\x6a\x71\xea\x96\x85\xd1\x23\x9b\x10\ +\xd5\x56\xe4\x40\x41\x9b\xc2\x2a\x2e\x58\x58\x0c\xbb\xb6\xea\x79\ +\x17\xd2\xc6\xae\x6a\x6d\xae\x56\x85\x31\x46\x36\xc1\xa2\x8d\x65\ +\xae\x19\xb5\x29\xbf\x37\x56\x2a\x2c\xa6\x5a\x1b\x7b\x16\x44\x6d\ +\x72\xaa\x36\x26\x67\x4b\x98\x44\x64\x13\x45\xda\x44\x17\x9e\x0a\ +\x6d\x86\x9f\x38\x0d\x0a\x8b\x49\x6a\x53\x7b\x6b\x97\xa9\x4d\x41\ +\xd5\xf6\x93\x38\x21\x4c\x34\xb2\x89\x45\x6d\x26\x1f\x9e\x36\xb4\ +\x81\xec\xe3\x18\x17\x16\x33\x69\x33\xdf\x9e\x98\x69\xd3\x54\x95\ +\x5c\x23\xe6\x7b\x89\x15\x43\xf0\x02\xf2\x44\x0e\xff\xb5\x7d\xff\ +\xf3\xc3\xfa\x40\x2e\x48\x0b\xd3\x07\x61\xf7\xf6\xf1\xee\x4d\x3f\ +\xf4\x9c\x36\xb2\x5e\x12\x75\x56\xb2\x18\xc3\x3d\x40\xf3\x17\xe4\ +\x6d\x80\xd6\xb0\x6b\x94\xb5\xd9\xaa\xca\x5c\x7a\x72\x85\xe9\x47\ +\x36\xa1\xa0\x0d\xbc\xaa\x18\xe8\xc2\x62\x84\xb4\x81\xa8\xca\x7f\ +\x67\x57\x20\xcc\x30\xb2\x09\x46\x6d\x20\xaa\x4a\x69\xa6\xb0\x18\ +\xa2\x36\x34\x55\x45\x1f\x9c\xca\x84\x21\x44\x36\x51\xa1\x0d\x4d\ +\x55\x05\x4d\x16\x16\x93\xa9\x0d\x56\x55\xe9\xbe\x44\xb1\x30\xa8\ +\xc8\x26\x36\xb4\xc1\xaa\xaa\xa3\xf9\xc2\x62\x66\xda\xf0\x55\x55\ +\x6c\xfb\xd5\x6c\x4d\x21\x9c\x33\xba\x01\xc2\xce\x96\x1c\xae\x0a\ +\x0b\x2d\x54\x35\x51\xf7\xbc\xaf\x14\x06\xb8\x92\x35\xa4\x8a\x82\ +\x87\xc2\x5a\x54\x55\xbd\xac\xd4\x0b\x43\x88\xac\x45\x55\x44\x5a\ +\x2d\xac\x69\x55\x94\x77\x6d\x24\x61\x26\x91\x35\xad\x8a\x4e\x4b\ +\x85\xf9\x50\x45\xfc\x50\x44\x15\xa6\x13\x99\x0f\x55\x2c\xa0\x17\ +\xe6\x4c\x15\x7d\xcf\x81\x41\x98\x50\x64\xce\x54\x71\x81\x58\x98\ +\x57\x55\x2c\x5b\x7a\x7b\xa6\xd9\x79\x41\x3b\x7f\x0f\x8d\xd7\x6f\ +\x2f\x87\x13\xc3\x38\xbb\x9f\x9f\x7f\x33\x0c\xe3\x1a\xfa\x6e\xf2\ +\x58\x05\xcb\x38\x6c\x27\x92\x3a\xde\x23\xe7\x7a\x89\x66\x19\x87\ +\x47\x98\x63\x5b\x74\x78\x7d\x73\x9e\xaa\xed\x58\x1b\x4e\x64\x0c\ +\xc2\x1c\x7b\xa2\xc3\x6e\x9a\xf9\xcb\x10\x8e\xe5\x81\x44\x46\x15\ +\xe6\xd8\x10\x1d\x09\xc7\xfc\x5f\x37\x72\xac\x10\x21\x32\x92\x30\ +\xc7\x6e\xe8\x08\xd9\x15\xf9\x42\x9f\x63\x91\xe6\x91\xd5\x0b\x73\ +\x6c\x85\x8e\x9c\x57\xa9\xaf\xcc\x3a\xd6\x69\x1b\x59\xa5\x30\xc7\ +\x3e\xe8\x88\x1a\x15\xfc\x52\xba\x63\xa9\x86\x91\xd5\x08\x73\x6c\ +\x82\x8e\xb4\x4b\xd9\xcb\x3e\x38\x56\x6b\x15\x59\xb1\x30\xc7\x0e\ +\xe8\x28\x58\x14\xbf\xb0\x8a\x63\xc1\x26\x91\x95\x09\x73\x3c\xfb\ +\x74\x74\xfc\x69\x5c\xba\xc8\xb1\x66\xfd\xc8\x0a\x84\x39\x9e\x77\ +\x3a\x6a\xe6\x94\x2e\x0e\xe6\x58\xb6\x72\x64\xb9\x67\x4d\x71\x9d\ +\x39\xd4\x21\xd2\x4f\x73\x6b\x0c\xc4\x33\x7f\x5b\x44\xed\x15\x28\ +\x6b\x0d\xe3\x5a\x81\xfa\x4a\x46\x27\x2d\xac\xdb\x52\x80\xff\x6d\ +\x7d\xd7\x96\x44\x27\xb2\x84\xb0\xee\x49\x01\xa9\xad\xa9\x2e\x2f\ +\x89\x42\x64\x5b\xc2\xba\x21\x05\x64\xff\x79\xa5\x2b\x4c\x22\x1d\ +\xd9\xaa\xb0\xee\x46\x01\x8d\x53\x04\xba\xc8\x24\xa2\x91\x2d\x0b\ +\xeb\x56\x14\xd0\x3b\xcd\xad\xeb\x4c\x22\x17\xd9\x82\xb0\xee\x43\ +\x01\xed\x53\xb5\xbb\xd4\x24\x42\x91\xcd\x85\x75\x13\x0a\xd8\x7c\ +\xdd\xa8\xab\x4d\x22\x11\xd9\x85\xb0\xee\x40\x01\xcb\xaf\xcc\x76\ +\xc1\x49\xd8\x23\x3b\x0b\xeb\xb3\xaf\x80\xfd\x65\x1f\xba\xe6\x24\ +\xbc\x91\x7d\x08\xeb\xf3\xae\x00\xca\xa5\x8b\xba\xec\x24\x8c\x91\ +\x7d\x0a\x7d\xc6\x55\xe0\x72\xb6\xfb\xf9\xf9\x37\xcb\x44\x33\x5e\ +\x94\xf4\xf5\xdb\x0b\xd7\x50\x74\x18\xaf\x03\xc9\xf2\xf7\xda\xa3\ +\xd9\x82\xe2\xf9\xf4\xf0\xf4\xce\x39\x1a\x7d\x90\x7e\x22\xe9\x32\ +\x12\x2f\xef\x4f\xef\x5f\x21\x2e\xd2\xec\x2c\x2f\xf0\x95\xb8\x17\ +\x76\x46\x41\x15\x3d\x32\xaa\x30\x1f\x79\x81\x57\x15\x73\xeb\x85\ +\xe9\xab\x22\x46\x46\x12\xd6\x74\x5e\x0d\x55\x15\x73\x8b\x85\x99\ +\xab\xa2\x44\x56\x2f\xac\xc5\xbc\xcc\x55\xd1\xb9\x95\xc2\xd0\x54\ +\x55\x47\x56\x29\xac\xa1\xbc\xd0\x54\x11\x51\xba\x8a\x80\x15\xc8\ +\xb6\xea\x9e\xf4\x35\xc2\xf0\xf3\x9a\x3c\x3d\xde\xbd\x39\xbb\x7a\ +\x81\xb7\x35\x6c\x31\x29\xae\xff\xab\x86\x9d\x8a\x95\xac\x58\x18\ +\x6c\x5e\x49\x1f\xb0\xda\x8a\xf0\x50\x58\x91\x03\x34\x6d\xa5\x91\ +\x95\x09\x43\xcb\xab\x7a\xde\xd1\xb4\xe5\xd3\x6a\x61\x2c\x73\x0d\ +\xa2\xad\x28\xb2\x02\x61\x20\x79\xb1\xcf\x2f\x88\xb6\x4c\x5a\x2a\ +\x4c\x74\x4e\x6d\xb5\xe5\x47\x96\x2b\xcc\x36\x2f\xb5\x79\xc4\xaf\ +\x0d\xbd\x30\x93\xb9\x33\xd1\x96\x19\x59\x96\x30\x93\xbc\xcc\x9f\ +\xe6\xa3\xb6\xef\x7f\x6c\x8f\x62\x0e\xdc\x5e\xe2\x78\xf2\x9e\xb9\ +\x2d\x13\x72\xc2\x48\x17\xa6\x99\xd7\xe1\xfe\xf8\x1a\xde\x02\xcc\ +\x5a\xf2\x7c\x7a\x08\xe1\x18\xac\x97\xf0\x18\x94\x35\x6c\xf1\xe5\ +\xdb\x50\xdb\xec\x41\xc7\xc3\x53\xd0\x96\x5c\xc9\x12\xc2\x14\x0e\ +\x31\xb9\xd2\x2a\x6b\xdb\x78\x20\x35\x6d\x1b\x58\x16\x56\xb4\x87\ +\xa6\xa0\x2d\x73\x70\x69\x6d\xdb\x91\x6d\x09\x93\x3b\xa6\xea\x53\ +\x50\x84\xb4\x55\x0c\x68\x55\x9b\x76\x61\xf4\x93\xcb\x03\xab\x36\ +\xe2\x20\x42\xda\x36\x22\x5b\x15\xc6\x7e\x10\x2c\xaa\x62\x88\xda\ +\x18\x33\xd5\xac\x4d\xa3\x30\x76\x55\x31\x15\xda\x84\x16\x42\x5e\ +\x6d\x6b\x91\x2d\x0b\xe3\x7a\x54\x51\x55\x31\x99\xda\x14\xde\x6a\ +\x4a\xd7\x26\x55\x98\x9a\xaa\x98\x0d\x6d\xca\x1f\xe6\x58\xb4\x2d\ +\x46\xb6\x20\x8c\xf8\x30\x26\xaa\x62\x66\xa7\x49\x19\x6e\x97\x48\ +\xd4\xc6\xb9\x97\x78\xb8\x3f\x9a\xdb\x9a\x61\xbe\xb9\x15\x68\xd3\ +\x72\x2d\x7b\x5e\x58\xdd\xd3\x01\xcd\xd3\x04\xc8\x9e\x64\xe0\xab\ +\x6d\x37\x0c\x43\xfc\xfb\xd2\x11\x85\xde\xac\x4b\x80\xa0\x6d\x84\ +\x32\xc9\x17\x85\x15\x0d\x04\x5b\xd5\x1a\x3e\x6a\xbb\x28\x2c\x73\ +\x08\x85\xcf\x55\xd2\x20\x68\x1b\x29\x9d\xf3\x73\x61\x39\xf7\x6c\ +\xae\xaa\x35\xda\xad\xed\x5c\xd8\xf6\x7d\x94\x3f\x02\x6b\x82\xa0\ +\x6d\x24\x47\xc1\x3e\x79\x53\x37\x55\xad\xd1\x56\x6d\x1f\x85\x2d\ +\xde\xc8\x70\xb7\xc2\x0a\x04\x6d\x23\x6b\x46\xf6\x8b\x3f\x73\x5f\ +\xd5\x1a\xf8\xb5\xed\x86\x61\x88\xff\xd4\x5c\x15\xce\xf7\xef\x10\ +\xb4\x8d\xc4\x82\x76\xbf\xfe\x7e\x19\x7f\x65\xae\x6a\x04\x47\xd8\ +\x08\x9a\xb6\x7f\x3b\xcf\xca\x48\x61\xee\x5b\x97\x00\x00\x00\x00\ +\x49\x45\x4e\x44\xae\x42\x60\x82\ +\x00\x00\x03\xef\ \x89\ \x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ \x00\x00\x10\x00\x00\x00\x10\x08\x06\x00\x00\x01\x68\xf4\xcf\xf7\ \x00\x00\x00\x06\x62\x4b\x47\x44\x00\xff\x00\xff\x00\xff\xa0\xbd\ \xa7\x93\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0b\x13\x00\x00\ -\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x02\xd4\x49\x44\x41\x54\x38\ -\xcb\x55\x92\x4f\x68\x5d\x65\x10\xc5\x7f\xdf\xbb\x37\xaf\x49\x48\ -\xd5\x04\x23\x22\x22\x88\x85\x08\x12\x05\x0b\xa2\x0b\xc1\x8d\x90\ -\x6c\x14\x04\xa1\xee\x94\x42\x15\x14\x22\xed\x56\x77\x6e\x0d\x82\ -\x2b\x05\x97\xba\x15\xdd\x58\x10\x0a\x82\xab\x5a\x50\x2b\x41\x24\ -\xb1\x31\x31\x79\x6d\xfe\xbc\x97\xfb\xde\xfd\xf7\xdd\xef\x7e\x33\ -\xe3\xe2\x3e\x0a\xce\x76\xe6\x9c\x39\x67\xe6\xb8\x67\x77\xde\xe4\ -\xa9\xfe\xe3\xb1\x57\xab\x6f\xbe\x9d\xdc\x48\x92\xb7\x0f\x3e\x5e\ -\xa9\xde\x7b\x68\xe8\x66\xb7\x5e\x30\x7f\x98\xe3\x5e\xdb\xdb\x58\ -\x3d\x8a\xc3\xdb\xdb\x61\x9f\x5c\x4b\x1c\x37\x57\xe1\xb8\x35\x1a\ -\x85\xdf\x2b\xdc\xeb\x7b\x1b\x3c\x98\x9c\xbf\xb5\x1b\x0e\x7f\xda\ -\x6a\xfe\xbe\x96\x02\x76\xa3\xbc\x49\xa1\xd5\xc5\x6c\x32\xde\x48\ -\x7f\xa9\xb7\x18\xc4\x13\x10\x83\x3f\xab\x24\x1d\x1c\x0c\xa0\x56\ -\x18\x04\xd8\x6b\x36\xdd\xfa\x3f\xef\xb3\x9c\x2e\xfe\x20\x26\x6b\ -\xa7\x92\x91\x49\x4e\xa9\x35\x99\xe6\x8c\x64\x7c\x2e\x2d\xb5\xde\ -\x3e\xf2\xc3\x0b\x07\xf1\x88\xa1\x64\xa8\x19\x00\x56\x44\x18\xc6\ -\x26\xbd\xe5\xb7\xae\x57\xea\x3f\x20\x76\x0d\x0c\x28\x04\xee\x34\ -\x70\x37\xe0\xf8\xf9\x19\x38\x89\x30\x8c\x39\x85\x2c\x50\x08\x8c\ -\x05\xc4\xde\xe5\x91\x99\x2f\xdd\x2b\xbb\x97\x79\x2c\x5d\xe6\x9c\ -\xeb\x7f\x5a\x5b\xb3\x91\x49\xfe\xdb\x71\x1c\x5d\x3a\x96\xd1\xce\ -\x99\x4c\x48\x1f\x4d\x1f\xee\xcf\xb8\xb4\x19\x6b\xc1\x69\xcc\x28\ -\xb4\xba\x38\xd1\x62\xbb\x52\x7f\xbd\xb1\xb0\x9e\x06\x6b\xab\x91\ -\x8c\xd9\x6f\xef\x31\x94\x8c\x68\x42\x34\x21\x8f\xc5\x1a\x13\x59\ -\x49\x8f\xe2\x30\x39\x8e\x23\x0e\xe2\x11\x5e\x43\xa7\x33\x2a\x8c\ -\x22\x64\xf2\x75\x3a\x88\x27\x8c\xa5\xc0\x6b\xc0\xb0\xce\x85\x57\ -\x38\x8b\x70\x1c\x9f\x48\xff\x6d\xef\x11\x25\x42\x04\x12\xa0\x35\ -\x38\x8d\x70\x18\xa0\xd4\x6f\x12\x7d\x6b\x69\x91\x91\xbc\x48\x26\ -\x1d\xed\xdd\x00\xbb\x4d\x67\xbd\xdf\x7b\x29\xa5\xd6\x0f\x19\xb6\ -\xbb\x8c\xe5\x33\x4a\x85\x89\x40\x21\x05\xb3\xbd\xf3\x2c\xa7\xb8\ -\x97\xef\xbc\xc3\x52\xf2\x00\x0b\xbd\x79\xfa\x6e\xe6\xaa\x73\xee\ -\x93\x68\x32\xd7\x58\xc0\x6b\x83\xb7\x40\x63\x81\x5a\x1b\x2a\xf3\ -\x75\xa5\xfe\xa3\x4a\xeb\xcd\xda\x1a\x82\xb5\x5d\x20\xe6\x7a\xb3\ -\x5f\xf4\x70\x57\x5a\x8b\xd4\xd6\x90\x6b\x49\xa1\x35\x5e\xbb\x21\ -\x41\x11\x13\x82\xb5\xf8\x29\x99\xd7\x66\x93\x68\xd7\xd2\xc6\xda\ -\xcf\x83\xc4\x2b\x7e\x0a\x3c\x93\x9c\x4c\x26\xd4\xd6\xd0\x5a\xec\ -\x2e\x03\x38\x1c\xd1\x04\x6b\x15\x1a\x85\x5a\xaf\x12\x2c\x71\xcf\ -\xef\x5c\x6a\x22\xd2\xaf\xd5\x53\x6a\x4d\xae\x15\xb5\x79\xc4\xf4\ -\x3e\xf8\x7e\x48\x1a\x85\x4a\xbb\xb0\x14\x0a\x5e\x4f\xd2\x41\x3c\ -\xd9\x6f\xad\xbd\xd0\x5a\xa4\x25\x76\x92\x55\xf9\x5f\x99\x75\xe7\ -\x2f\xa7\xff\x19\x0b\xe4\x02\xc1\xf6\x1d\xb7\x9f\x5b\x25\xe8\xaf\ -\x44\x4b\x88\xd3\x47\x76\x9a\xbb\xd2\xe9\xe6\x52\x21\x8b\x90\x4d\ -\xc1\xad\x09\x33\xee\xe9\x94\x42\xfe\xa0\xd2\x79\x6a\xfd\x0e\xaf\ -\x6b\xb4\x06\x6a\x20\x40\x34\x08\xd6\x11\x14\xd2\xc9\x0f\x06\xf0\ -\x3d\x73\xbd\x37\x98\xef\x49\x8a\x03\x7a\x04\xcc\xd6\x09\x06\xa5\ -\x3c\x49\xa5\x97\xa9\xf4\x55\xbc\xae\xd0\x1a\xb4\xf6\x17\x6a\x3f\ -\xd2\x73\x5f\x31\xeb\x76\x59\x48\x60\x29\x85\xc5\x94\xff\x00\xe1\ -\x78\x1f\x4c\x73\x1c\xbc\x8b\x00\x00\x00\x00\x49\x45\x4e\x44\xae\ -\x42\x60\x82\ +\x0b\x13\x01\x00\x9a\x9c\x18\x00\x00\x03\x8f\x49\x44\x41\x54\x38\ +\xcb\x3d\x91\xdf\x4f\x5b\x75\x00\xc5\xcf\xf7\x7e\x2f\xb4\xbd\xb7\ +\xdc\x76\x8c\xfe\x80\x5b\xe4\x87\x73\xcc\x84\x8c\x9f\x0d\x83\x25\ +\x2e\x6e\x3e\x8c\x44\xb3\x64\x66\x21\x6a\x62\x8c\xba\x68\xa6\x0f\ +\xea\x93\x0f\x26\x8b\x0f\xc6\x7f\xc0\x44\x97\xc5\x2c\x53\x5f\xdc\ +\xe2\xa3\xc8\xa0\xb0\x1f\x22\x14\xc1\x6d\x08\x6d\x2d\x90\x51\xda\ +\x4b\x27\x3f\xd6\x52\xee\xbd\x50\x7a\xbf\xbd\x5f\x1f\x10\x5e\x4f\ +\xce\x39\xf9\xe4\x1c\xca\x18\x43\x4f\x4f\xdf\x23\xc4\xe2\x09\x30\ +\xc6\x38\xb1\x2c\x0b\x00\x20\xe6\xb6\xf2\x7c\xf9\xc9\x0a\x08\x63\ +\x0c\xdb\xdb\x7a\x6f\xb1\x54\x9c\x30\x0c\x03\x88\x8c\xde\x43\xb8\ +\xbb\x8f\xf7\xf6\xbe\xc4\x1f\x8c\xff\x0e\x58\x96\x85\x72\xb9\x8c\ +\x8c\xa6\xfd\x64\x59\x16\xc0\x18\xc3\xf4\xcc\x43\x5e\x2e\x97\xf9\ +\x48\x64\xcc\xa4\xe1\x70\x8f\xd6\x15\xee\x54\xa2\xd1\x28\x8e\x54\ +\x7b\x2b\x71\xee\xec\x79\xb3\xed\x64\x37\x9f\x8b\xcd\x9b\x1b\xcf\ +\x36\xf7\x23\x96\x65\x29\x8c\x31\xcc\xfc\xf5\x68\x28\x91\x4c\xf2\ +\x7c\x61\x8b\xdb\xb6\xcd\x4d\xd3\xac\x21\xa9\x95\x34\x04\x41\x40\ +\x3c\x9e\xe4\x27\x4e\x1c\x43\xd0\x1f\x00\x07\xc7\xfd\x07\xe3\xe8\ +\xec\x6c\xbf\x86\xef\xbe\xbd\xfe\x59\x7b\x5b\x98\x07\x82\xf5\xfc\ +\xe2\xeb\x03\x5c\xd7\x75\x3e\x7a\xf7\x1e\x1f\x1e\x89\xb4\x65\xff\ +\x7d\x0a\x32\x3c\x3c\x06\x70\xe0\xe7\x5b\xb7\x22\xa9\xe5\xd4\x39\ +\xc3\x30\x71\xe3\x87\x6b\xde\x50\x5d\xa8\x20\xcb\xf2\x3e\xc3\xff\ +\x1c\xd0\xb4\xec\xab\xd3\x33\x0f\xe7\x0d\xd3\xec\x38\xd0\x0e\x0c\ +\x4e\x9b\x73\x8c\xdd\xbd\xcf\x57\x32\x69\x5e\x2e\x97\xb9\xb6\xaa\ +\x4d\x70\xce\x21\x02\x00\xa5\xb4\xf8\xeb\xe0\x90\xd5\xd2\xf2\x02\ +\x8e\x56\x1f\x85\x6d\x73\xc4\x13\xc9\xde\xbd\xa2\x75\x41\xd4\x56\ +\xb3\x58\x5b\x5f\xbf\xd1\xdc\xd4\x24\x2a\x1e\x05\x2e\xa7\x13\x8f\ +\x67\x67\xe1\x74\x38\xd0\xd4\xd4\x10\x11\x76\x4c\xb3\xc6\xd0\xf5\ +\x77\x1a\x9b\x1b\xa0\x54\x29\xd0\x75\x1d\x6b\x6b\x1b\xa8\xac\xac\ +\x7c\xf3\xf1\xdf\xb3\x26\xe9\xeb\x3d\xc3\xd7\x37\x36\x90\xcb\xe7\ +\x30\x15\x1d\xc7\xc2\xe2\x22\x82\xc1\x20\x37\x4c\x5d\x68\x6d\x6d\ +\x85\xf0\xd1\xc7\x57\xfa\x8e\x78\xbd\x68\xa8\x7f\x0e\xfd\xe7\x5f\ +\x83\xa2\x28\x30\x77\x8c\xe7\x5d\x2e\x17\x00\x40\xf0\xf9\x7c\x93\ +\xef\xbd\xff\xee\x8b\xb5\x75\xb5\x7b\xaa\xaa\x22\x12\x19\x4b\x29\ +\x8a\xb2\x1c\xee\xea\x86\x57\xf1\xec\x3f\x0e\x00\x9c\x73\x00\xa0\ +\x84\x10\x4e\x08\xb1\x4b\xa5\x52\xf3\x64\xf4\xcf\x39\x87\xc3\x21\ +\xf9\x03\x35\xf0\x7a\xbd\x90\x5d\x92\xb5\xbb\xbb\x7b\xdd\xed\x96\ +\x3f\xa1\x54\xb4\x38\xb7\x0f\x0b\x08\x00\x0a\x80\x51\x4a\xb1\xb0\ +\xb0\xf8\xf9\x93\xe5\xd4\xd7\x75\x75\xb5\x50\x3c\x55\x90\xdd\x32\ +\x14\xb7\x82\x4a\xb1\x02\x7b\xa5\x22\xe6\x62\x71\x80\xe3\xc7\x8e\ +\xf6\x93\x57\x0e\x09\x00\x80\x31\xe6\x9f\x98\x8c\xfe\x51\x51\xe1\ +\x38\xe6\xf3\xd7\x40\x92\x9c\xa8\xaa\xaa\x82\xec\x92\x40\x29\x45\ +\x2e\x9f\x43\x2c\xfe\x0f\xac\x92\x05\x8f\xc7\xb3\x13\x0c\x04\xde\ +\x12\xe7\x63\x09\x08\x44\x00\x21\xe4\xed\x8c\x96\xb9\x19\x0a\x85\ +\x20\x49\x4e\xb8\x24\x17\xdc\x6e\x37\x24\xa7\x0b\x84\x10\xa4\xd3\ +\x69\x24\x17\x96\x40\x29\x85\x3f\xe0\x5b\x57\xd5\xba\x16\x4a\xe9\ +\x96\x98\xcf\xe5\x4e\xdd\xbe\xfd\xcb\x37\x53\x53\xd3\x5d\x82\x40\ +\x50\xab\xd6\xe2\x8d\x81\x01\x5c\xba\x74\x11\x94\x52\x94\x4a\x25\ +\xc4\x12\x09\x68\x99\x55\x54\x57\x57\x83\x08\x18\x5a\x5d\xd5\xfa\ +\x4d\xd3\x40\x30\x18\x84\x90\xcf\x17\xb6\x0c\xdd\x14\x08\x00\x66\ +\x31\x2c\x25\x97\x70\xf5\xea\x97\x50\xd5\x46\x74\x74\xf6\xe0\xb7\ +\x3b\x77\xa0\x6f\x1b\x08\xd5\xab\x00\xe1\xdf\xeb\xba\xde\xaf\xaa\ +\x21\xb4\x1c\x3f\x0e\x49\x92\x40\x46\x22\x63\x10\x45\x11\xb1\xb9\ +\xc4\x85\xc1\xc1\xc1\x9b\xf9\x7c\xde\x23\x08\xc2\xc1\x26\x28\x14\ +\x0a\xf8\xe0\xc3\xcb\x38\xfb\xca\x99\x2f\x4c\x73\xe7\x2b\x59\x96\ +\xd1\xd4\xd0\x08\x49\x92\x20\x08\x02\xc8\xc8\xf0\x28\x00\x80\x08\ +\x84\x1a\xe6\x4e\x59\xd7\x0d\x64\xb3\xd9\xe6\xcd\xcd\x67\x97\x19\ +\xb3\x5e\xf6\xfb\xfd\x4f\x4f\x9f\x3e\xf5\xa9\xc7\xab\xa4\x82\x81\ +\x00\xfc\x3e\x3f\x6c\xdb\x3e\x1c\xfe\x3f\x11\x5f\xc4\xbb\xcd\x16\ +\x27\xa0\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\x82\ +\x00\x00\x07\x22\ +\x89\ +\x50\x4e\x47\x0d\x0a\x1a\x0a\x00\x00\x00\x0d\x49\x48\x44\x52\x00\ +\x00\x00\x90\x00\x00\x00\x90\x08\x06\x00\x00\x00\xe7\x46\xe2\xb8\ +\x00\x00\x00\x04\x73\x42\x49\x54\x08\x08\x08\x08\x7c\x08\x64\x88\ +\x00\x00\x00\x09\x70\x48\x59\x73\x00\x00\x0e\xc4\x00\x00\x0e\xc4\ +\x01\x95\x2b\x0e\x1b\x00\x00\x06\xc4\x49\x44\x41\x54\x78\x9c\xed\ +\x9d\xbb\x71\x1b\x31\x14\x45\xb1\x1e\x05\x6a\x80\x33\x4a\xdc\x04\ +\x4b\x70\xe8\x12\x14\xb3\x2c\xc5\x2a\xc1\xa1\x4b\x50\x13\x4e\x38\ +\xa3\x06\x94\xd1\x81\x06\x36\x04\xee\x2e\x7e\xef\x73\x1f\xf6\x9d\ +\x50\x23\xed\x82\x78\x87\x97\x58\x40\x00\x97\x97\xf7\xa7\x5b\x08\ +\x21\x5c\x4e\xd7\x25\x18\xe4\xf9\xf1\xed\xa6\xdd\x86\x51\x5e\x3f\ +\xce\x26\xfb\xfe\xe5\xfd\xe9\xb6\x44\x81\x22\x2e\x92\x3c\xd6\x04\ +\x4a\x9d\x59\xf2\x1f\x44\x5c\x24\x39\xac\x08\xb4\xe6\xc9\xa6\x40\ +\x11\x17\x89\x1f\x74\x81\xb6\xfc\xb8\x9c\xae\xcb\x52\xfa\xa5\xf4\ +\x97\xa9\x1b\x26\x81\x05\x91\x50\x05\xaa\x71\xa2\x5a\xa0\xf4\x8f\ +\x46\x1b\xa6\x01\xb2\x48\x68\x02\xd5\xb8\x10\x3d\xf8\xd2\xf0\x5a\ +\x89\xd2\x0b\x58\x03\x51\x24\x14\x81\x7a\xea\xdf\x2d\x50\x7e\x21\ +\x6b\x20\x89\xa4\x2d\x50\x6b\xdd\xd3\x9a\xdf\x35\xbc\x47\xa2\xfc\ +\xa2\x96\x40\x10\x49\x4b\x20\x8a\x5a\x93\x09\xb4\x76\x71\x4b\x68\ +\x8a\x24\x2d\xd0\x48\x8d\xf3\xfa\xae\x36\x7c\x54\xa2\xb5\x1b\x59\ +\x41\x43\x24\x29\x81\x38\xea\xca\x26\xd0\xd6\x0d\xad\x20\x29\x12\ +\xb7\x40\x54\xf5\x5c\xab\xe5\xb7\xda\x5f\xec\xe5\xe5\xfd\xe9\x46\ +\x29\xa4\x14\xda\x03\x5b\x0a\x24\xfa\xfe\x81\xf3\xe2\x29\x96\x16\ +\x6d\xf3\xf4\x79\xfd\x38\x2f\x08\x83\xed\x5a\x38\xa4\xd9\xaa\xdb\ +\xa6\x40\x97\xd3\xf5\x6e\xa1\x95\x02\x64\x91\xf6\x24\x89\x89\x84\ +\x2c\x92\x46\xd2\x8b\x25\x50\x0e\x92\x48\x2d\x52\x20\x8a\xc4\x2d\ +\xce\x5e\x8d\x76\x05\xe2\x4a\xa1\x14\x4d\x91\x46\x24\x40\x10\x09\ +\x61\x6c\xa9\x96\x40\x39\x92\x22\x51\x16\x5d\x43\x24\x49\x71\x4a\ +\xf5\x28\x0a\x24\x91\x42\x29\x9c\x22\x71\x16\x59\x42\x24\x84\xc4\ +\xc9\x81\x49\xa0\x1c\x4a\x91\x34\xe6\x74\x28\xef\xa9\x25\x4e\x4d\ +\xdf\x57\x09\x24\x9d\x42\x29\x23\x22\x21\x2c\x4f\x8c\xb4\x01\x31\ +\x71\x72\x60\x13\x28\xa7\x45\x24\xa4\x27\xa4\x1e\x91\x10\xc4\xa9\ +\x7d\xc3\x56\x0b\xa4\x99\x42\x29\x7b\x22\x21\x89\x93\x53\x23\x12\ +\x42\xff\xb6\x62\x26\x81\x72\x52\x91\x90\xc5\xc9\x59\x13\x09\x4d\ +\x9c\x96\xe1\xc2\xea\x5a\x18\xc5\x85\xa5\x40\xeb\xfc\x5a\xa2\x48\ +\x3f\xfe\xfc\xd4\x6e\xca\x10\x4d\x02\xa1\x62\x6d\xe1\xf3\xf9\xf1\ +\xed\x86\x9a\x9a\xad\x21\xd1\xfc\x11\x86\x32\x16\xca\x41\x98\x19\ +\x2e\x81\xdc\xb6\x5e\xcc\x8e\x81\xb6\x40\x14\x09\xa9\x2d\x7b\xf4\ +\x0c\x51\xba\x04\x42\x4d\xa1\x14\x04\x91\xac\x88\x33\xc2\x74\x09\ +\x94\xa3\x21\x92\x45\x71\x7a\x1f\x90\xba\x05\xb2\x90\x42\x29\x12\ +\x22\x59\x14\x67\x94\xe9\x13\x28\x87\x43\x24\xeb\xe2\x8c\x4c\xcf\ +\x0c\x09\x64\x2d\x85\x52\x28\x44\xb2\x2e\x0e\x05\x87\x4b\xa0\x9c\ +\x1e\x91\x66\x12\x67\x74\x72\x78\x58\x20\xcb\x29\x94\x52\x23\xd2\ +\x4c\xe2\x50\x71\xf8\x04\xca\x59\x13\x69\x56\x71\x28\x96\xa6\x48\ +\x96\x32\x10\xd7\xc8\x46\xb1\xb6\x3c\xa2\x85\x27\xd0\x0a\xb3\x26\ +\x4e\x0a\xd5\x9b\x9e\x4c\xa0\x19\xc6\x42\x47\x10\x87\x1a\x4f\xa0\ +\x70\x3c\x71\x28\x87\x1c\xa4\x02\x59\x4b\xa1\xa3\x89\xc3\xc1\x21\ +\x13\xe8\xc8\xe2\x50\x3f\xf0\x90\x0b\x84\x9c\x42\x47\x16\x87\x8b\ +\x43\x24\x90\x8b\xf3\x09\xc7\x74\x0b\x8b\x40\x28\x29\xe4\xe2\xf0\ +\x33\x65\x02\xb9\x38\xf7\x70\x4d\xf6\xb2\x09\xa4\x91\x42\x2e\x8e\ +\x3c\x53\x24\x90\x8b\xb3\x0f\xe7\x52\xd3\x03\x67\xe7\xff\xfe\x6e\ +\x7f\xdf\x93\x75\x7e\x7f\xff\x15\x42\x38\xb3\x5d\xdf\xd4\xae\xce\ +\x59\xe1\x5a\xb8\x8d\xb5\xe5\xbc\x3e\xfb\xc6\x42\x5f\xd5\x2e\xc3\ +\xfd\x26\xe6\xbc\x3e\xab\x40\x2e\x8f\x1e\x52\x52\x8a\x6c\x6d\x76\ +\x91\xca\x58\x4d\x21\x36\x81\x5c\x1a\x3d\x24\x65\x14\x3b\x5c\xc1\ +\x85\x2a\x63\x31\x85\x58\x04\x72\x59\xf4\x90\x96\x50\xf4\x78\x17\ +\x17\xab\x8c\xb5\x14\x22\x17\xc8\x25\xd1\x43\x43\x3e\xf1\x03\xa6\ +\x5c\xb0\x32\x96\x52\x88\x54\x20\x97\x43\x0f\x2d\xe9\x54\x8e\xb8\ +\x73\xd1\xca\x58\x49\x21\x32\x81\x5c\x0a\x3d\x34\x65\x53\x3b\x64\ +\xd3\x85\x2b\x63\x21\x85\x48\x04\x72\x19\xf4\xd0\x96\x4c\xf5\x98\ +\x5f\x17\xaf\x8c\xb6\x20\x25\x86\x05\x72\x09\xf4\x40\x90\x4b\xfd\ +\xa0\x71\x17\xb0\x0c\x82\x28\x5b\x0c\x09\xe4\xc5\xd7\x03\x45\x2a\ +\xf5\x04\x0a\xc1\x45\xac\x01\x45\x98\x9c\x6e\x81\xbc\xe8\x7a\x20\ +\xc9\x04\x91\x40\x21\xb8\x90\x35\x20\x89\x13\xe9\xda\x95\xc1\xbd\ +\x8b\xc0\xb1\x83\x6f\xeb\x71\x86\x98\x62\x67\xea\xd1\x40\xfa\x04\ +\x68\x1e\x03\x71\x8f\x55\x7c\x2c\x54\x06\xe9\x53\xa3\x49\x20\x97\ +\x67\x5e\x44\x1f\xe3\x5d\x24\x7d\x50\x52\xa8\x5a\x20\x97\x66\x5e\ +\x54\x96\x32\x5c\x28\x7d\x10\x52\xa8\x4a\x20\x97\x65\x5e\x54\xff\ +\x9d\xc3\xc5\xd2\x47\x3b\x85\x8a\x02\xb9\x24\xf3\x02\xf1\x2f\xad\ +\x2e\x98\x3e\x9a\x29\xb4\x2b\x90\xcb\x31\x2f\x50\xdb\x7a\x5c\x34\ +\x7d\xb4\x52\x68\x53\x20\x97\x62\x5e\x20\xb7\x36\xbb\x70\xfa\x68\ +\xa4\xd0\xaa\x40\x2e\xc3\xbc\x40\x1f\xef\xe2\xe2\xe9\x23\x9d\x42\ +\x77\x02\xb9\x04\xf3\x62\xe2\x88\x3b\x17\x50\x1f\xc9\x14\xfa\x22\ +\x90\x17\x7f\x5e\x4c\x1d\xf3\xeb\x22\xea\x23\x95\x42\xff\x04\xf2\ +\xa2\xcf\x8b\xc9\xaf\x3a\x70\x21\xf5\x91\x48\xa1\x6f\x21\x78\xb1\ +\x67\x86\x5b\xa2\xe5\xf9\xf1\xed\xc6\x59\x60\x89\x6f\x2d\xfc\xfc\ +\x4e\x2c\x9b\x48\x7c\x9f\x1a\x67\xff\x3c\x58\x97\xc7\x32\xaf\x1f\ +\xe7\xe5\x47\xe0\xef\x23\xce\x1a\xfb\xc6\x42\x05\xa4\x3f\xd2\x5f\ +\xde\x9f\x6e\xe6\xbe\x74\xd7\xd3\xe7\x9e\x19\xc7\x82\x9e\x40\x02\ +\x20\x88\xc3\x95\x42\x2c\x02\x79\xfa\x7c\x82\x20\x0e\x37\x9e\x40\ +\x0c\xa0\x8a\xc3\x91\x42\xe4\x02\x1d\x39\x7d\x50\xc5\xe1\xc4\x13\ +\x88\x00\x4b\xe2\x50\xa7\x10\xa9\x40\x47\x4b\x1f\x4b\xe2\x70\xe1\ +\x09\xd4\x81\x75\x71\x28\x53\x88\x4c\xa0\x23\xa4\x8f\x75\x71\x38\ +\x80\x39\xa5\x15\x9d\xd9\xe4\xa1\x7a\xc3\x93\x08\x34\x6b\xfa\xa4\ +\xd2\x3c\x3f\xbe\xdd\xb4\x0f\x32\x40\xc4\xc7\x40\x2b\xec\xa5\x4d\ +\x94\x68\x86\x44\xa2\x18\x0b\x0d\x0b\x34\x53\xfa\xb4\x48\x31\x93\ +\x48\x23\x78\x02\x85\x31\x09\xac\x8b\x34\x9a\x42\x43\x02\x59\x4f\ +\x1f\xca\xa2\x5b\x17\xa9\x97\x43\x26\x10\x67\x91\x2d\x8a\x34\x92\ +\x42\xdd\x02\x59\x4c\x1f\xc9\xa2\x5a\x14\xa9\x87\x43\x24\x90\x66\ +\x11\xad\x88\xd4\x9b\x42\x5d\x02\x59\x49\x1f\xa4\xa2\x59\x11\xa9\ +\x95\x29\x13\x08\xb9\x48\xc8\x22\xf5\xa4\x50\xb3\x40\xc8\xe9\x83\ +\x58\x94\x2d\xfe\xcf\x6a\xf3\x6f\xeb\xe1\xc4\xfc\x5a\x58\xdc\xf3\ +\x64\x49\x1e\x64\x5a\x03\xa2\x49\x20\xb4\xf4\x49\xe3\xd6\xe2\x5a\ +\xd5\xeb\xc7\x79\xb9\x9c\xae\x0b\xd7\x96\x1b\x09\x4c\x8e\x81\xf6\ +\x3a\x1c\x79\x8c\x11\x59\x6b\x5b\x7c\x4d\x08\x6f\xd2\x96\xb1\x50\ +\xb5\x40\x08\x2f\xac\xe5\x9d\x8a\x28\x52\x4d\x5b\x90\x44\xaa\xc1\ +\x44\x02\x8d\x44\x3c\x82\x48\x3d\xf7\xd6\x16\xa9\x36\x85\xaa\x04\ +\xd2\x7a\x11\x94\x63\x03\x0d\x91\x28\xee\xa5\x2d\x52\x09\xc8\x04\ +\xe2\x1c\x54\x4a\x88\xc4\x71\x6d\x0d\x91\x6a\x52\xa8\x28\x90\x64\ +\x83\x25\x9f\x46\x38\x44\x92\x48\x37\xb4\x44\x82\x48\x20\xcd\xc7\ +\x58\x0a\x91\x34\xc6\x57\x52\x22\x95\x52\x68\x57\x20\xee\xc6\x21\ +\xcd\x7f\xf4\x88\x84\xf0\x84\xa7\x9d\x48\x2a\x09\x84\x24\x4e\x4e\ +\x8d\x48\x08\xe2\xe4\x70\x8a\xb4\x97\x42\x9b\x1d\xc1\xd1\x10\x0e\ +\x71\x24\x67\x9f\x11\xc5\xd9\x82\xba\x7e\x5b\xb5\x13\x59\x0b\xb3\ +\x3e\x5d\x1f\x82\x2d\x79\x42\xa0\xef\xf3\x2d\x21\x57\x6f\x40\x65\ +\xaf\x84\x34\xd2\xeb\x5f\xd6\x44\x8a\x50\xd4\x74\xad\x9e\x2c\x02\ +\x69\x3c\x8e\x4b\x73\x54\x91\xf2\xda\xde\x75\xc2\xc8\x0d\x34\x3e\ +\xa6\xb4\x57\xe0\x8f\x26\x12\x8b\x40\x08\xf3\x38\xda\x1c\x49\xa4\ +\xb4\xde\x5f\x5e\x74\xeb\xc5\x10\x06\xc6\x28\x02\x45\x8e\x20\xd2\ +\xb0\x40\x08\xe2\x44\xd0\x04\x8a\xcc\x2e\x52\x74\xe0\xdf\x8b\xac\ +\xf9\x43\x24\x71\x22\xa8\x02\x45\x66\x15\xa9\x49\x20\x44\x71\x22\ +\xe8\x02\x45\x66\x14\xe9\x72\xba\x2e\xbb\xd3\xdf\xc8\xe2\x44\xac\ +\x08\x14\x99\x49\xa4\x4d\x81\x2c\x88\x13\xb1\x26\x50\x64\x16\x91\ +\x96\xf4\x07\x96\xc4\x89\x58\x15\x28\x62\x5d\xa4\xbf\xa8\xcc\xde\ +\x47\x76\xb8\xb3\xea\x00\x00\x00\x00\x49\x45\x4e\x44\xae\x42\x60\ +\x82\ " qt_resource_name = "\ @@ -1081,19 +1543,30 @@ qt_resource_name = "\ \x07\x03\x7d\xc3\ \x00\x69\ \x00\x6d\x00\x61\x00\x67\x00\x65\x00\x73\ -\x00\x18\ -\x02\x47\xd6\x47\ -\x00\x63\ -\x00\x61\x00\x6e\x00\x2d\x00\x69\x00\x63\x00\x6f\x00\x6e\x00\x2d\x00\x32\x00\x34\x00\x70\x00\x78\x00\x2d\x00\x79\x00\x65\x00\x6c\ -\x00\x6c\x00\x6f\x00\x77\x00\x2e\x00\x70\x00\x6e\x00\x67\ +\x00\x0e\ +\x0a\x51\x2d\xe7\ +\x00\x69\ +\x00\x64\x00\x65\x00\x6e\x00\x74\x00\x69\x00\x74\x00\x69\x00\x65\x00\x73\x00\x2e\x00\x70\x00\x6e\x00\x67\ +\x00\x09\ +\x09\x6b\xb7\xc7\ +\x00\x69\ +\x00\x6e\x00\x62\x00\x6f\x00\x78\x00\x2e\x00\x70\x00\x6e\x00\x67\ +\x00\x0b\ +\x0a\xd0\x22\xa7\ +\x00\x72\ +\x00\x65\x00\x64\x00\x69\x00\x63\x00\x6f\x00\x6e\x00\x2e\x00\x70\x00\x6e\x00\x67\ \x00\x08\ \x0c\x57\x58\x67\ \x00\x73\ \x00\x65\x00\x6e\x00\x74\x00\x2e\x00\x70\x00\x6e\x00\x67\ -\x00\x0e\ -\x02\x47\x93\x47\ -\x00\x79\ -\x00\x65\x00\x6c\x00\x6c\x00\x6f\x00\x77\x00\x69\x00\x63\x00\x6f\x00\x6e\x00\x2e\x00\x70\x00\x6e\x00\x67\ +\x00\x10\ +\x0c\xc3\x45\x27\ +\x00\x71\ +\x00\x69\x00\x64\x00\x65\x00\x6e\x00\x74\x00\x69\x00\x63\x00\x6f\x00\x6e\x00\x5f\x00\x78\x00\x2e\x00\x70\x00\x6e\x00\x67\ +\x00\x0d\ +\x02\xe8\x12\x87\ +\x00\x62\ +\x00\x6c\x00\x61\x00\x63\x00\x6b\x00\x6c\x00\x69\x00\x73\x00\x74\x00\x2e\x00\x70\x00\x6e\x00\x67\ \x00\x08\ \x0c\x47\x58\x67\ \x00\x73\ @@ -1103,77 +1576,94 @@ qt_resource_name = "\ \x00\x61\ \x00\x64\x00\x64\x00\x72\x00\x65\x00\x73\x00\x73\x00\x62\x00\x6f\x00\x6f\x00\x6b\x00\x2e\x00\x70\x00\x6e\x00\x67\ \x00\x11\ -\x03\x89\x73\x27\ -\x00\x63\ -\x00\x61\x00\x6e\x00\x2d\x00\x69\x00\x63\x00\x6f\x00\x6e\x00\x2d\x00\x31\x00\x36\x00\x70\x00\x78\x00\x2e\x00\x70\x00\x6e\x00\x67\ +\x07\x34\x2d\xc7\ +\x00\x6e\ +\x00\x65\x00\x74\x00\x77\x00\x6f\x00\x72\x00\x6b\x00\x73\x00\x74\x00\x61\x00\x74\x00\x75\x00\x73\x00\x2e\x00\x70\x00\x6e\x00\x67\ \ -\x00\x09\ -\x09\x6b\xb7\xc7\ -\x00\x69\ -\x00\x6e\x00\x62\x00\x6f\x00\x78\x00\x2e\x00\x70\x00\x6e\x00\x67\ +\x00\x18\ +\x02\x47\xd6\x47\ +\x00\x63\ +\x00\x61\x00\x6e\x00\x2d\x00\x69\x00\x63\x00\x6f\x00\x6e\x00\x2d\x00\x32\x00\x34\x00\x70\x00\x78\x00\x2d\x00\x79\x00\x65\x00\x6c\ +\x00\x6c\x00\x6f\x00\x77\x00\x2e\x00\x70\x00\x6e\x00\x67\ \x00\x11\ \x02\xa0\x44\xa7\ \x00\x73\ \x00\x75\x00\x62\x00\x73\x00\x63\x00\x72\x00\x69\x00\x70\x00\x74\x00\x69\x00\x6f\x00\x6e\x00\x73\x00\x2e\x00\x70\x00\x6e\x00\x67\ \ +\x00\x0e\ +\x09\x39\xff\x47\ +\x00\x71\ +\x00\x69\x00\x64\x00\x65\x00\x6e\x00\x74\x00\x69\x00\x63\x00\x6f\x00\x6e\x00\x2e\x00\x70\x00\x6e\x00\x67\ +\x00\x11\ +\x05\x89\x73\x07\ +\x00\x63\ +\x00\x61\x00\x6e\x00\x2d\x00\x69\x00\x63\x00\x6f\x00\x6e\x00\x2d\x00\x32\x00\x34\x00\x70\x00\x78\x00\x2e\x00\x70\x00\x6e\x00\x67\ +\ \x00\x15\ \x0c\xfc\x45\x87\ \x00\x63\ \x00\x61\x00\x6e\x00\x2d\x00\x69\x00\x63\x00\x6f\x00\x6e\x00\x2d\x00\x32\x00\x34\x00\x70\x00\x78\x00\x2d\x00\x72\x00\x65\x00\x64\ \x00\x2e\x00\x70\x00\x6e\x00\x67\ -\x00\x11\ -\x05\x89\x73\x07\ -\x00\x63\ -\x00\x61\x00\x6e\x00\x2d\x00\x69\x00\x63\x00\x6f\x00\x6e\x00\x2d\x00\x32\x00\x34\x00\x70\x00\x78\x00\x2e\x00\x70\x00\x6e\x00\x67\ -\ -\x00\x0e\ -\x0a\x51\x2d\xe7\ -\x00\x69\ -\x00\x64\x00\x65\x00\x6e\x00\x74\x00\x69\x00\x74\x00\x69\x00\x65\x00\x73\x00\x2e\x00\x70\x00\x6e\x00\x67\ -\x00\x0b\ -\x0a\xd0\x22\xa7\ -\x00\x72\ -\x00\x65\x00\x64\x00\x69\x00\x63\x00\x6f\x00\x6e\x00\x2e\x00\x70\x00\x6e\x00\x67\ \x00\x0d\ -\x02\xe8\x12\x87\ -\x00\x62\ -\x00\x6c\x00\x61\x00\x63\x00\x6b\x00\x6c\x00\x69\x00\x73\x00\x74\x00\x2e\x00\x70\x00\x6e\x00\x67\ -\x00\x11\ -\x07\x34\x2d\xc7\ -\x00\x6e\ -\x00\x65\x00\x74\x00\x77\x00\x6f\x00\x72\x00\x6b\x00\x73\x00\x74\x00\x61\x00\x74\x00\x75\x00\x73\x00\x2e\x00\x70\x00\x6e\x00\x67\ -\ +\x07\x76\xdf\x07\ +\x00\x67\ +\x00\x72\x00\x65\x00\x65\x00\x6e\x00\x69\x00\x63\x00\x6f\x00\x6e\x00\x2e\x00\x70\x00\x6e\x00\x67\ \x00\x17\ \x00\xd3\x62\xc7\ \x00\x63\ \x00\x61\x00\x6e\x00\x2d\x00\x69\x00\x63\x00\x6f\x00\x6e\x00\x2d\x00\x32\x00\x34\x00\x70\x00\x78\x00\x2d\x00\x67\x00\x72\x00\x65\ \x00\x65\x00\x6e\x00\x2e\x00\x70\x00\x6e\x00\x67\ -\x00\x0d\ -\x07\x76\xdf\x07\ +\x00\x11\ +\x02\x8c\x5e\x67\ +\x00\x6e\ +\x00\x6f\x00\x5f\x00\x69\x00\x64\x00\x65\x00\x6e\x00\x74\x00\x69\x00\x63\x00\x6f\x00\x6e\x00\x73\x00\x2e\x00\x70\x00\x6e\x00\x67\ +\ +\x00\x0e\ +\x02\x47\x93\x47\ +\x00\x79\ +\x00\x65\x00\x6c\x00\x6c\x00\x6f\x00\x77\x00\x69\x00\x63\x00\x6f\x00\x6e\x00\x2e\x00\x70\x00\x6e\x00\x67\ +\x00\x12\ +\x03\xf4\x2e\xc7\ +\x00\x71\ +\x00\x69\x00\x64\x00\x65\x00\x6e\x00\x74\x00\x69\x00\x63\x00\x6f\x00\x6e\x00\x5f\x00\x74\x00\x77\x00\x6f\x00\x2e\x00\x70\x00\x6e\ \x00\x67\ -\x00\x72\x00\x65\x00\x65\x00\x6e\x00\x69\x00\x63\x00\x6f\x00\x6e\x00\x2e\x00\x70\x00\x6e\x00\x67\ +\x00\x11\ +\x03\x89\x73\x27\ +\x00\x63\ +\x00\x61\x00\x6e\x00\x2d\x00\x69\x00\x63\x00\x6f\x00\x6e\x00\x2d\x00\x31\x00\x36\x00\x70\x00\x78\x00\x2e\x00\x70\x00\x6e\x00\x67\ +\ +\x00\x14\ +\x07\x12\xd0\xa7\ +\x00\x71\ +\x00\x69\x00\x64\x00\x65\x00\x6e\x00\x74\x00\x69\x00\x63\x00\x6f\x00\x6e\x00\x5f\x00\x74\x00\x77\x00\x6f\x00\x5f\x00\x78\x00\x2e\ +\x00\x70\x00\x6e\x00\x67\ " qt_resource_struct = "\ \x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\ \x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\ -\x00\x00\x00\x18\x00\x02\x00\x00\x00\x10\x00\x00\x00\x03\ -\x00\x00\x02\x18\x00\x00\x00\x00\x00\x01\x00\x00\x35\x5d\ -\x00\x00\x00\x76\x00\x00\x00\x00\x00\x01\x00\x00\x09\x8e\ +\x00\x00\x00\x18\x00\x02\x00\x00\x00\x15\x00\x00\x00\x03\ +\x00\x00\x02\x36\x00\x00\x00\x00\x00\x01\x00\x00\x3f\x80\ +\x00\x00\x02\x92\x00\x00\x00\x00\x00\x01\x00\x00\x47\xb7\ +\x00\x00\x01\x3e\x00\x00\x00\x00\x00\x01\x00\x00\x1d\x49\ +\x00\x00\x02\x6a\x00\x00\x00\x00\x00\x01\x00\x00\x46\xee\ +\x00\x00\x01\x74\x00\x00\x00\x00\x00\x01\x00\x00\x24\xaf\ +\x00\x00\x00\xbc\x00\x00\x00\x00\x00\x01\x00\x00\x11\xfc\ +\x00\x00\x02\xde\x00\x00\x00\x00\x00\x01\x00\x00\x51\x02\ +\x00\x00\x02\xb4\x00\x00\x00\x00\x00\x01\x00\x00\x4a\x35\ +\x00\x00\x00\xf2\x00\x00\x00\x00\x00\x01\x00\x00\x18\x2b\ +\x00\x00\x01\xbe\x00\x00\x00\x00\x00\x01\x00\x00\x2d\x9f\ +\x00\x00\x03\x06\x00\x00\x00\x00\x00\x01\x00\x00\x54\xf5\ +\x00\x00\x01\x16\x00\x00\x00\x00\x00\x01\x00\x00\x1a\xd0\ +\x00\x00\x02\x16\x00\x00\x00\x00\x00\x01\x00\x00\x3c\x48\ +\x00\x00\x01\x9c\x00\x00\x00\x00\x00\x01\x00\x00\x27\x2a\ +\x00\x00\x00\x4c\x00\x00\x00\x00\x00\x01\x00\x00\x03\x6a\ \x00\x00\x00\x2a\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\ -\x00\x00\x01\x12\x00\x00\x00\x00\x00\x01\x00\x00\x18\x92\ -\x00\x00\x01\xd0\x00\x00\x00\x00\x00\x01\x00\x00\x2f\xf0\ -\x00\x00\x00\xd2\x00\x00\x00\x00\x00\x01\x00\x00\x11\xec\ -\x00\x00\x00\xae\x00\x00\x00\x00\x00\x01\x00\x00\x0f\x47\ -\x00\x00\x01\x6a\x00\x00\x00\x00\x00\x01\x00\x00\x22\x76\ -\x00\x00\x01\xf0\x00\x00\x00\x00\x00\x01\x00\x00\x32\xe4\ -\x00\x00\x02\x4c\x00\x00\x00\x00\x00\x01\x00\x00\x3c\xcb\ -\x00\x00\x00\xfa\x00\x00\x00\x00\x00\x01\x00\x00\x15\xdf\ -\x00\x00\x01\x92\x00\x00\x00\x00\x00\x01\x00\x00\x29\xb6\ -\x00\x00\x01\xb4\x00\x00\x00\x00\x00\x01\x00\x00\x2d\x20\ -\x00\x00\x00\x98\x00\x00\x00\x00\x00\x01\x00\x00\x0c\x0c\ -\x00\x00\x00\x60\x00\x00\x00\x00\x00\x01\x00\x00\x07\x66\ -\x00\x00\x01\x3a\x00\x00\x00\x00\x00\x01\x00\x00\x1b\x0d\ +\x00\x00\x00\x64\x00\x00\x00\x00\x00\x01\x00\x00\x06\x1d\ +\x00\x00\x00\xdc\x00\x00\x00\x00\x00\x01\x00\x00\x14\xf0\ +\x00\x00\x00\x80\x00\x00\x00\x00\x00\x01\x00\x00\x08\xed\ +\x00\x00\x00\x96\x00\x00\x00\x00\x00\x01\x00\x00\x0b\x15\ +\x00\x00\x01\xe6\x00\x00\x00\x00\x00\x01\x00\x00\x34\xdf\ " def qInitResources(): diff --git a/src/bitmessageqt/bitmessageui.py b/src/bitmessageqt/bitmessageui.py index 8f8b8951..e186fba3 100644 --- a/src/bitmessageqt/bitmessageui.py +++ b/src/bitmessageqt/bitmessageui.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'bitmessageui.ui' # -# Created: Wed Sep 18 17:38:54 2013 +# Created: Sat Sep 21 09:44:07 2013 # by: PyQt4 UI code generator 4.10.2 # # WARNING! All changes made in this file will be lost! diff --git a/src/bitmessageqt/settings.py b/src/bitmessageqt/settings.py index 290c0cb6..054aead7 100644 --- a/src/bitmessageqt/settings.py +++ b/src/bitmessageqt/settings.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'settings.ui' # -# Created: Wed Sep 18 17:38:55 2013 +# Created: Sat Sep 21 09:44:07 2013 # by: PyQt4 UI code generator 4.10.2 # # WARNING! All changes made in this file will be lost! diff --git a/src/helper_startup.py b/src/helper_startup.py index 24dc0156..b9b264f6 100644 --- a/src/helper_startup.py +++ b/src/helper_startup.py @@ -71,6 +71,10 @@ def loadConfig(): 'bitmessagesettings', 'maxacceptablepayloadlengthextrabytes', '0') shared.config.set('bitmessagesettings', 'dontconnect', 'true') shared.config.set('bitmessagesettings', 'userlocale', 'system') + shared.config.set('bitmessagesettings', 'identicon', 'None') + import random, string + shared.config.set('bitmessagesettings', 'identiconsuffix', ''.join(random.choice("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz") for x in range(12))) # a twelve character pseudo-password to salt the identicons + shared.config.set('bitmessagesettings', 'avatars', 'false') ensureNamecoinOptions() if storeConfigFilesInSameDirectoryAsProgramByDefault: From d6bd2b393827feb0f9958e842658ec18c458568c Mon Sep 17 00:00:00 2001 From: sendiulo Date: Sat, 21 Sep 2013 14:31:47 +0200 Subject: [PATCH 12/22] - GUI settings done --- src/bitmessageqt/__init__.py | 24 +++++++++++- src/bitmessageqt/bitmessage_icons_rc.py | 2 +- src/bitmessageqt/bitmessageui.py | 2 +- src/bitmessageqt/settings.py | 38 +++++-------------- src/bitmessageqt/settings.ui | 49 +------------------------ 5 files changed, 36 insertions(+), 79 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index dd1c1a7a..5895c98f 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -2241,10 +2241,20 @@ class MyForm(QtGui.QMainWindow): self.settingsDialogInstance.ui.checkBoxStartInTray.isChecked())) shared.config.set('bitmessagesettings', 'willinglysendtomobile', str( self.settingsDialogInstance.ui.checkBoxWillinglySendToMobile.isChecked())) + lang_ind = int(self.settingsDialogInstance.ui.languageComboBox.currentIndex()) if not languages[lang_ind] == 'other': shared.config.set('bitmessagesettings', 'userlocale', languages[lang_ind]) + + ### + curr_index = self.settingsDialogInstance.ui.comboBoxIdenticonStyle.currentIndex() + shared.config.set('bitmessagesettings', 'identicon', str(self.settingsDialogInstance.ui.comboBoxIdenticonStyle.itemData( + curr_index , Qt.UserRole).toString())) + shared.config.set('bitmessagesettings', 'identiconsuffix', str( + self.settingsDialogInstance.ui.lineEditIdenticonSuffix.text())) + shared.config.set('bitmessagesettings', 'avatars', str( + self.settingsDialogInstance.ui.checkBoxLoadAvatars.isChecked())) if int(shared.config.get('bitmessagesettings', 'port')) != int(self.settingsDialogInstance.ui.lineEditTCPPort.text()): if not shared.safeConfigGetBoolean('bitmessagesettings', 'dontconnect'): @@ -3104,7 +3114,6 @@ class MyForm(QtGui.QMainWindow): # set the icon thisTableWidget.item( currentRow, 0).setIcon(avatarize(addressAtCurrentRow)) - ### shared.reloadBroadcastSendersForWhichImWatching() self.rerenderSubscriptions() self.rerenderComboBoxSendFrom() @@ -3349,6 +3358,19 @@ class settingsDialog(QtGui.QDialog): curr_index = languages.index('other') self.ui.languageComboBox.setCurrentIndex(curr_index) + ### + self.ui.comboBoxIdenticonStyle.addItem(QIcon(":/newPrefix/images/no_identicons.png"), _translate("settingsDialog", "None"), "none") + self.ui.comboBoxIdenticonStyle.addItem(QIcon(":/newPrefix/images/qidenticon.png"), _translate("settingsDialog", "QIdenticon"), "qidenticon") + self.ui.comboBoxIdenticonStyle.addItem(QIcon(":/newPrefix/images/qidenticon_x.png"), _translate("settingsDialog", "QIdenticon (transparent)"), "qidenticon_x") + self.ui.comboBoxIdenticonStyle.addItem(QIcon(":/newPrefix/images/qidenticon_two.png"), _translate("settingsDialog", "QIdenticon two"), "qidenticon_two") + self.ui.comboBoxIdenticonStyle.addItem(QIcon(":/newPrefix/images/qidenticon_two_x.png"), _translate("settingsDialog", "QIdenticon two (transparent)"), "qidenticon_two_x") + curr_index = self.ui.comboBoxIdenticonStyle.findData(str(shared.config.get('bitmessagesettings', 'identicon')), Qt.UserRole) + self.ui.comboBoxIdenticonStyle.setCurrentIndex(curr_index) + self.ui.lineEditIdenticonSuffix.setText( + str(shared.config.get('bitmessagesettings', 'identiconsuffix'))) + self.ui.checkBoxLoadAvatars.setChecked( + shared.safeConfigGetBoolean('bitmessagesettings', 'avatars')) + if shared.appdata == '': self.ui.checkBoxPortableMode.setChecked(True) if 'darwin' in sys.platform: diff --git a/src/bitmessageqt/bitmessage_icons_rc.py b/src/bitmessageqt/bitmessage_icons_rc.py index 2db046a4..bb0a02c0 100644 --- a/src/bitmessageqt/bitmessage_icons_rc.py +++ b/src/bitmessageqt/bitmessage_icons_rc.py @@ -2,7 +2,7 @@ # Resource object code # -# Created: Sa 21. Sep 09:44:10 2013 +# Created: Sa 21. Sep 13:45:58 2013 # by: The Resource Compiler for PyQt (Qt v4.8.4) # # WARNING! All changes made in this file will be lost! diff --git a/src/bitmessageqt/bitmessageui.py b/src/bitmessageqt/bitmessageui.py index e186fba3..10fb1f8f 100644 --- a/src/bitmessageqt/bitmessageui.py +++ b/src/bitmessageqt/bitmessageui.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'bitmessageui.ui' # -# Created: Sat Sep 21 09:44:07 2013 +# Created: Sat Sep 21 13:55:14 2013 # by: PyQt4 UI code generator 4.10.2 # # WARNING! All changes made in this file will be lost! diff --git a/src/bitmessageqt/settings.py b/src/bitmessageqt/settings.py index 054aead7..d80f5e80 100644 --- a/src/bitmessageqt/settings.py +++ b/src/bitmessageqt/settings.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'settings.ui' # -# Created: Sat Sep 21 09:44:07 2013 +# Created: Sat Sep 21 13:55:15 2013 # by: PyQt4 UI code generator 4.10.2 # # WARNING! All changes made in this file will be lost! @@ -83,31 +83,16 @@ class Ui_settingsDialog(object): self.gridLayout_5.addWidget(self.groupBox, 7, 1, 4, 1) self.groupBox_3 = QtGui.QGroupBox(self.tabUserInterface) self.groupBox_3.setObjectName(_fromUtf8("groupBox_3")) - self.comboBox = QtGui.QComboBox(self.groupBox_3) - self.comboBox.setGeometry(QtCore.QRect(20, 20, 251, 31)) - self.comboBox.setIconSize(QtCore.QSize(24, 24)) - self.comboBox.setObjectName(_fromUtf8("comboBox")) - icon = QtGui.QIcon() - icon.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/no_identicons.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) - self.comboBox.addItem(icon, _fromUtf8("")) - icon1 = QtGui.QIcon() - icon1.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/qidenticon.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) - self.comboBox.addItem(icon1, _fromUtf8("")) - icon2 = QtGui.QIcon() - icon2.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/qidenticon_x.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) - self.comboBox.addItem(icon2, _fromUtf8("")) - icon3 = QtGui.QIcon() - icon3.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/qidenticon_two.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) - self.comboBox.addItem(icon3, _fromUtf8("")) - icon4 = QtGui.QIcon() - icon4.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/qidenticon_two_x.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) - self.comboBox.addItem(icon4, _fromUtf8("")) + self.comboBoxIdenticonStyle = QtGui.QComboBox(self.groupBox_3) + self.comboBoxIdenticonStyle.setGeometry(QtCore.QRect(20, 20, 251, 31)) + self.comboBoxIdenticonStyle.setIconSize(QtCore.QSize(24, 24)) + self.comboBoxIdenticonStyle.setObjectName(_fromUtf8("comboBoxIdenticonStyle")) self.checkBoxLoadAvatars = QtGui.QCheckBox(self.groupBox_3) self.checkBoxLoadAvatars.setGeometry(QtCore.QRect(20, 50, 121, 18)) self.checkBoxLoadAvatars.setObjectName(_fromUtf8("checkBoxLoadAvatars")) - self.lineEdit = QtGui.QLineEdit(self.groupBox_3) - self.lineEdit.setGeometry(QtCore.QRect(140, 50, 131, 16)) - self.lineEdit.setObjectName(_fromUtf8("lineEdit")) + self.lineEditIdenticonSuffix = QtGui.QLineEdit(self.groupBox_3) + self.lineEditIdenticonSuffix.setGeometry(QtCore.QRect(140, 50, 131, 16)) + self.lineEditIdenticonSuffix.setObjectName(_fromUtf8("lineEditIdenticonSuffix")) self.gridLayout_5.addWidget(self.groupBox_3, 7, 0, 4, 1) self.tabWidgetSettings.addTab(self.tabUserInterface, _fromUtf8("")) self.tabNetworkSettings = QtGui.QWidget() @@ -380,13 +365,8 @@ class Ui_settingsDialog(object): self.languageComboBox.setItemText(7, _translate("settingsDialog", "Pirate English", "en_pirate")) self.languageComboBox.setItemText(8, _translate("settingsDialog", "Other (set in keys.dat)", "other")) self.groupBox_3.setTitle(_translate("settingsDialog", "Identicons (with example image)", None)) - self.comboBox.setItemText(0, _translate("settingsDialog", "no identicons", None)) - self.comboBox.setItemText(1, _translate("settingsDialog", "qidenticon", None)) - self.comboBox.setItemText(2, _translate("settingsDialog", "qidenticon_x", None)) - self.comboBox.setItemText(3, _translate("settingsDialog", "qidenticon_two", None)) - self.comboBox.setItemText(4, _translate("settingsDialog", "qidenticon_two_x", None)) self.checkBoxLoadAvatars.setText(_translate("settingsDialog", "Load avatar images", None)) - self.lineEdit.setToolTip(_translate("settingsDialog", "

The content of this text field will be appended to the BM-address before creating the hash for the identicons. By default it is filled with a random string to make the identicons in your client unique, otherwise the identicon could be an attack vector if an adversary creates an address resulting in a similar identicon. If you keep this string (or any other random or non-random string) you will be able to keep the same identicons.

", None)) + self.lineEditIdenticonSuffix.setToolTip(_translate("settingsDialog", "

The content of this text field will be appended to the BM-address before creating the hash for the identicons. By default it is filled with a random string to make the identicons in your client unique, otherwise the identicon could be an attack vector if an adversary creates an address resulting in a similar identicon. If you keep this string (or any other random or non-random string) you will be able to keep the same identicons.

", None)) self.tabWidgetSettings.setTabText(self.tabWidgetSettings.indexOf(self.tabUserInterface), _translate("settingsDialog", "User Interface", None)) self.groupBox1.setTitle(_translate("settingsDialog", "Listening port", None)) self.label.setText(_translate("settingsDialog", "Listen for connections on port:", None)) diff --git a/src/bitmessageqt/settings.ui b/src/bitmessageqt/settings.ui index a5e6e4e6..dd330f2c 100644 --- a/src/bitmessageqt/settings.ui +++ b/src/bitmessageqt/settings.ui @@ -155,7 +155,7 @@ Identicons (with example image) - + 20 @@ -170,51 +170,6 @@ 24 - - - no identicons - - - - :/newPrefix/images/no_identicons.png:/newPrefix/images/no_identicons.png - - - - - qidenticon - - - - :/newPrefix/images/qidenticon.png:/newPrefix/images/qidenticon.png - - - - - qidenticon_x - - - - :/newPrefix/images/qidenticon_x.png:/newPrefix/images/qidenticon_x.png - - - - - qidenticon_two - - - - :/newPrefix/images/qidenticon_two.png:/newPrefix/images/qidenticon_two.png - - - - - qidenticon_two_x - - - - :/newPrefix/images/qidenticon_two_x.png:/newPrefix/images/qidenticon_two_x.png - - @@ -229,7 +184,7 @@ Load avatar images - + 140 From cd2c55dd2da9ed5a41812e9bd42aeb31cea6dd50 Mon Sep 17 00:00:00 2001 From: sendiulo Date: Sat, 21 Sep 2013 14:49:30 +0200 Subject: [PATCH 13/22] removed bug: sent icon wrongly put into column 0 instead of 1 --- src/bitmessageqt/__init__.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 5895c98f..7dd19764 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -112,9 +112,6 @@ def identiconize(address): idcon = QtGui.QIcon() idcon.addPixmap(pix, QtGui.QIcon.Normal, QtGui.QIcon.Off) return idcon - elif identicon_lib == 'empty': - idcon = QtGui.QIcon(":/newPrefix/images/no_identicons.png") - return idcon elif identicon_lib in ['False', 'false', 'None', 'none']: idcon = QtGui.QIcon() return idcon @@ -174,9 +171,6 @@ def avatarize(address, fallBackToIdenticon = False): except: # default to no identicons identicon_lib = False - if identicon_lib == 'empty': - idcon = QtGui.QIcon(":/newPrefix/images/no_identicons.png") - return idcon return idcon class MyForm(QtGui.QMainWindow): @@ -1690,7 +1684,7 @@ class MyForm(QtGui.QMainWindow): self.ui.tableWidgetSent.item( i, 1).setText(unicode(fromLabel, 'utf-8')) self.ui.tableWidgetSent.item( - i, 0).setIcon(avatarize(fromAddress, True)) + i, 1).setIcon(avatarize(fromAddress, True)) def rerenderSentToLabels(self): for i in range(self.ui.tableWidgetSent.rowCount()): @@ -3358,8 +3352,7 @@ class settingsDialog(QtGui.QDialog): curr_index = languages.index('other') self.ui.languageComboBox.setCurrentIndex(curr_index) - ### - self.ui.comboBoxIdenticonStyle.addItem(QIcon(":/newPrefix/images/no_identicons.png"), _translate("settingsDialog", "None"), "none") + self.ui.comboBoxIdenticonStyle.addItem(_translate("settingsDialog", "None"), "none") self.ui.comboBoxIdenticonStyle.addItem(QIcon(":/newPrefix/images/qidenticon.png"), _translate("settingsDialog", "QIdenticon"), "qidenticon") self.ui.comboBoxIdenticonStyle.addItem(QIcon(":/newPrefix/images/qidenticon_x.png"), _translate("settingsDialog", "QIdenticon (transparent)"), "qidenticon_x") self.ui.comboBoxIdenticonStyle.addItem(QIcon(":/newPrefix/images/qidenticon_two.png"), _translate("settingsDialog", "QIdenticon two"), "qidenticon_two") From 21005fb0a8f8f025c97e0220e9cbee946f697031 Mon Sep 17 00:00:00 2001 From: sendiulo Date: Sat, 21 Sep 2013 17:06:54 +0200 Subject: [PATCH 14/22] - add the settings also for old users --- src/bitmessageqt/__init__.py | 4 +--- src/class_sqlThread.py | 7 +++++++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 4c03378c..dc9627eb 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -2171,12 +2171,10 @@ class MyForm(QtGui.QMainWindow): shared.config.set('bitmessagesettings', 'willinglysendtomobile', str( self.settingsDialogInstance.ui.checkBoxWillinglySendToMobile.isChecked())) - lang_ind = int(self.settingsDialogInstance.ui.languageComboBox.currentIndex()) if not languages[lang_ind] == 'other': shared.config.set('bitmessagesettings', 'userlocale', languages[lang_ind]) - - ### + curr_index = self.settingsDialogInstance.ui.comboBoxIdenticonStyle.currentIndex() shared.config.set('bitmessagesettings', 'identicon', str(self.settingsDialogInstance.ui.comboBoxIdenticonStyle.itemData( curr_index , Qt.UserRole).toString())) diff --git a/src/class_sqlThread.py b/src/class_sqlThread.py index 34109172..9764e3c7 100644 --- a/src/class_sqlThread.py +++ b/src/class_sqlThread.py @@ -208,6 +208,13 @@ class sqlThread(threading.Thread): if not shared.config.has_option('bitmessagesettings', 'userlocale'): shared.config.set('bitmessagesettings', 'userlocale', 'system') + if not shared.config.has_option('bitmessagesettings', 'identicon'): + shared.config.set('bitmessagesettings', 'identicon', 'None') + if not shared.config.has_option('bitmessagesettings', 'identiconsuffix'): + import random, string + shared.config.set('bitmessagesettings', 'identiconsuffix', ''.join(random.choice("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz") for x in range(12))) # a twelve character pseudo-password to salt the identicons + if not shared.config.has_option('bitmessagesettings', 'avatars'): + shared.config.set('bitmessagesettings', 'avatars', 'false') if not shared.config.has_option('bitmessagesettings', 'sendoutgoingconnections'): shared.config.set('bitmessagesettings', 'sendoutgoingconnections', 'True') From 41c3b73343e713ae404d489f71d8e4392ed0cf74 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Sat, 26 Oct 2013 18:49:22 -0400 Subject: [PATCH 15/22] comment --- src/bitmessagemain.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 2294d398..3173981a 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -928,7 +928,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): networkStatus = 'connectedAndReceivingIncomingConnections' return json.dumps({'networkConnections':len(shared.connectedHostsList),'numberOfMessagesProcessed':shared.numberOfMessagesProcessed, 'numberOfBroadcastsProcessed':shared.numberOfBroadcastsProcessed, 'numberOfPubkeysProcessed':shared.numberOfPubkeysProcessed, 'networkStatus':networkStatus, 'softwareName':'PyBitmessage','softwareVersion':shared.softwareVersion}, indent=4, separators=(',', ': ')) elif method == 'decodeAddress': - #decode an address + # Return a meaningful decoding of an address. if len(params) != 1: raise APIError(0, 'I need 1 parameter!') address, = params From f7ef2b4e051fcd0cc2d15818235ac769f8484818 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Fri, 1 Nov 2013 19:25:24 -0400 Subject: [PATCH 16/22] various changes to Identicon code --- src/bitmessageqt/__init__.py | 315 ++++++++++++++--------------------- src/bitmessageqt/settings.py | 183 ++++++++------------ src/bitmessageqt/settings.ui | 216 ++++++++---------------- src/class_sqlThread.py | 16 +- src/helper_startup.py | 6 +- 5 files changed, 288 insertions(+), 448 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index ce3160aa..910f73ff 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -29,6 +29,7 @@ import sys from time import strftime, localtime, gmtime import time import os +import hashlib from pyelliptic.openssl import OpenSSL import pickle import platform @@ -64,20 +65,19 @@ def identiconize(address): # 3fd4bf901b9d4ea1394f0fb358725b28 try: - identicon_lib = shared.config.get('bitmessagesettings', 'identicon') + identicon_lib = shared.config.get('bitmessagesettings', 'identiconlib') except: - # default to no identicons - identicon_lib = False + # default to qidenticon_two_x + identicon_lib = 'qidenticon_two_x' + + # As an 'identiconsuffix' you could put "@bitmessge.ch" or "@bm.addr" to make it compatible with other identicon generators. (Note however, that E-Mail programs might convert the BM-address to lowercase first.) + # It can be used as a pseudo-password to salt the generation of the identicons to decrease the risk + # of attacks where someone creates an address to mimic someone else's identicon. + identiconsuffix = shared.config.get('bitmessagesettings', 'identiconsuffix') - try: - # As an 'identiconsuffix' you could put "@bitmessge.ch" or "@bm.addr" to make it compatible with other identicon generators. (Note however, that E-Mail programs might convert the BM-address to lowercase first.) - # It can be used as a pseudo-password to salt the generation of the identicons to decrease the risk - # of attacks where someone creates an address to mimic someone else's identicon. - # If not set yet it will be filled by a random string. - # Another good idea would be to fill it with the private key of one of your addresses (because you don't need to memorize it then). - identiconsuffix = shared.config.get('bitmessagesettings', 'identiconsuffix') - except: - identiconsuffix = '' + if not shared.config.getboolean('bitmessagesettings', 'useidenticons'): + idcon = QtGui.QIcon() + return idcon if (identicon_lib[:len('qidenticon')] == 'qidenticon'): # print identicon_lib @@ -86,7 +86,6 @@ def identiconize(address): # Licesensed under FreeBSD License. # stripped from PIL and uses QT instead (by sendiulo, same license) import qidenticon - import hashlib hash = hashlib.md5(addBMIfNotPresent(address)+identiconsuffix).hexdigest() use_two_colors = (identicon_lib[:len('qidenticon_two')] == 'qidenticon_two') opacity = int(not((identicon_lib == 'qidenticon_x') | (identicon_lib == 'qidenticon_two_x') | (identicon_lib == 'qidenticon_b') | (identicon_lib == 'qidenticon_two_b')))*255 @@ -113,66 +112,50 @@ def identiconize(address): idcon = QtGui.QIcon() idcon.addPixmap(pix, QtGui.QIcon.Normal, QtGui.QIcon.Off) return idcon - elif identicon_lib in ['False', 'false', 'None', 'none']: - idcon = QtGui.QIcon() - return idcon - else: - # default to no identicons - idcon = QtGui.QIcon() - return idcon -def avatarize(address, fallBackToIdenticon = False): +def avatarize(address): """ loads a supported image for the given address' hash form 'avatars' folder falls back to default avatar if 'default.*' file exists - falls back to identiconize(address) if second argument fallBackToIdenticon == True + falls back to identiconize(address) """ idcon = QtGui.QIcon() - if shared.safeConfigGetBoolean('bitmessagesettings', 'avatars'): - import hashlib - hash = hashlib.md5(addBMIfNotPresent(address)).hexdigest() - str_broadcast_subscribers = '[Broadcast subscribers]' - if address == str_broadcast_subscribers: - # don't hash [Broadcast subscribers] - hash = address - # http://pyqt.sourceforge.net/Docs/PyQt4/qimagereader.html#supportedImageFormats - # print QImageReader.supportedImageFormats () - # QImageReader.supportedImageFormats () - extensions = ['PNG', 'GIF', 'JPG', 'JPEG', 'SVG', 'BMP', 'MNG', 'PBM', 'PGM', 'PPM', 'TIFF', 'XBM', 'XPM', 'TGA'] - # try to find a specific avatar - for ext in extensions: - lower_hash = shared.appdata + 'avatars/' + hash + '.' + ext.lower() - upper_hash = shared.appdata + 'avatars/' + hash + '.' + ext.upper() - if os.path.isfile(lower_hash): - # print 'found avatar of ', address - idcon.addFile(lower_hash) - return idcon - elif os.path.isfile(upper_hash): - # print 'found avatar of ', address - idcon.addFile(upper_hash) - return idcon - # if we haven't found any, try to find a default avatar - for ext in extensions: - lower_default = shared.appdata + 'avatars/' + 'default.' + ext.lower() - upper_default = shared.appdata + 'avatars/' + 'default.' + ext.upper() - if os.path.isfile(lower_default): - default = lower_default - idcon.addFile(lower_default) - return idcon - elif os.path.isfile(upper_default): - default = upper_default - idcon.addFile(upper_default) - return idcon - # if avatars are deactivated or none found - if fallBackToIdenticon: - return identiconize(address) - else: - try: - identicon_lib = shared.config.get('bitmessagesettings', 'identicon') - except: - # default to no identicons - identicon_lib = False - return idcon + hash = hashlib.md5(addBMIfNotPresent(address)).hexdigest() + str_broadcast_subscribers = '[Broadcast subscribers]' + if address == str_broadcast_subscribers: + # don't hash [Broadcast subscribers] + hash = address + # http://pyqt.sourceforge.net/Docs/PyQt4/qimagereader.html#supportedImageFormats + # print QImageReader.supportedImageFormats () + # QImageReader.supportedImageFormats () + extensions = ['PNG', 'GIF', 'JPG', 'JPEG', 'SVG', 'BMP', 'MNG', 'PBM', 'PGM', 'PPM', 'TIFF', 'XBM', 'XPM', 'TGA'] + # try to find a specific avatar + for ext in extensions: + lower_hash = shared.appdata + 'avatars/' + hash + '.' + ext.lower() + upper_hash = shared.appdata + 'avatars/' + hash + '.' + ext.upper() + if os.path.isfile(lower_hash): + # print 'found avatar of ', address + idcon.addFile(lower_hash) + return idcon + elif os.path.isfile(upper_hash): + # print 'found avatar of ', address + idcon.addFile(upper_hash) + return idcon + # if we haven't found any, try to find a default avatar + for ext in extensions: + lower_default = shared.appdata + 'avatars/' + 'default.' + ext.lower() + upper_default = shared.appdata + 'avatars/' + 'default.' + ext.upper() + if os.path.isfile(lower_default): + default = lower_default + idcon.addFile(lower_default) + return idcon + elif os.path.isfile(upper_default): + default = upper_default + idcon.addFile(upper_default) + return idcon + # If no avatar is found + return identiconize(address) + class MyForm(QtGui.QMainWindow): @@ -462,7 +445,6 @@ class MyForm(QtGui.QMainWindow): newItem.setTextColor(QtGui.QColor(128, 128, 128)) if shared.safeConfigGetBoolean(addressInKeysFile, 'mailinglist'): newItem.setTextColor(QtGui.QColor(137, 04, 177)) # magenta - newItem.setIcon(identiconize(addressInKeysFile)) self.ui.tableWidgetYourIdentities.setItem(0, 1, newItem) newItem = QtGui.QTableWidgetItem(str( decodeAddress(addressInKeysFile)[2])) @@ -531,7 +513,6 @@ class MyForm(QtGui.QMainWindow): self.ui.tableWidgetYourIdentities.setIconSize(QtCore.QSize(identicon_size, identicon_size)) self.ui.tableWidgetSubscriptions.setIconSize(QtCore.QSize(identicon_size, identicon_size)) self.ui.tableWidgetAddressBook.setIconSize(QtCore.QSize(identicon_size, identicon_size)) - #self.ui.tableWidgetWhitelist.setIconSize(QtCore.QSize(identicon_size, identicon_size)) self.ui.tableWidgetBlacklist.setIconSize(QtCore.QSize(identicon_size, identicon_size)) self.UISignalThread = UISignaler() @@ -713,30 +694,29 @@ class MyForm(QtGui.QMainWindow): for row in queryreturn: toAddress, fromAddress, subject, status, ackdata, lastactiontime = row subject = shared.fixPotentiallyInvalidUTF8Data(subject) - #message = shared.fixPotentiallyInvalidUTF8Data(message) - try: + + if shared.config.has_section(fromAddress): fromLabel = shared.config.get(fromAddress, 'label') - except: - fromLabel = '' if fromLabel == '': fromLabel = fromAddress toLabel = '' queryreturn = sqlQuery( '''select label from addressbook where address=?''', toAddress) - if queryreturn != []: for row in queryreturn: toLabel, = row + + if toLabel == '': + if shared.config.has_section(toAddress): + toLabel = shared.config.get(toAddress, 'label') + if toLabel == '': + toLabel = toAddress self.ui.tableWidgetSent.insertRow(0) - if toLabel == '': - newItem = QtGui.QTableWidgetItem(unicode(toAddress, 'utf-8')) - newItem.setToolTip(unicode(toAddress, 'utf-8')) - else: - newItem = QtGui.QTableWidgetItem(unicode(toLabel, 'utf-8')) - newItem.setToolTip(unicode(toLabel, 'utf-8')) - newItem.setIcon(avatarize(toAddress, True)) + newItem = QtGui.QTableWidgetItem(unicode(toLabel, 'utf-8')) + newItem.setToolTip(unicode(toLabel, 'utf-8')) + newItem.setIcon(avatarize(toAddress)) newItem.setData(Qt.UserRole, str(toAddress)) newItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) @@ -748,7 +728,7 @@ class MyForm(QtGui.QMainWindow): else: newItem = QtGui.QTableWidgetItem(unicode(fromLabel, 'utf-8')) newItem.setToolTip(unicode(fromLabel, 'utf-8')) - newItem.setIcon(avatarize(fromAddress, True)) + newItem.setIcon(avatarize(fromAddress)) newItem.setData(Qt.UserRole, str(fromAddress)) newItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) @@ -837,7 +817,6 @@ class MyForm(QtGui.QMainWindow): for row in queryreturn: msgid, toAddress, fromAddress, subject, received, read = row subject = shared.fixPotentiallyInvalidUTF8Data(subject) - #message = shared.fixPotentiallyInvalidUTF8Data(message) try: if toAddress == self.str_broadcast_subscribers: toLabel = self.str_broadcast_subscribers @@ -848,17 +827,13 @@ class MyForm(QtGui.QMainWindow): if toLabel == '': toLabel = toAddress - try: # try to get the from label fom YourIdentites (for chan messages) + fromLabel = '' + if shared.config.has_section(fromAddress): fromLabel = shared.config.get(fromAddress, 'label') - checkIfChan = True - except: - fromLabel = '' - checkIfChan = False - if fromLabel == '': # If this address wasn't in our address book... + if fromLabel == '': # If the fromAddress isn't one of our addresses queryreturn = sqlQuery( '''select label from addressbook where address=?''', fromAddress) - if queryreturn != []: for row in queryreturn: fromLabel, = row @@ -866,16 +841,17 @@ class MyForm(QtGui.QMainWindow): if fromLabel == '': # If this address wasn't in our address book... queryReturn = sqlQuery( '''select label from subscriptions where address=?''', fromAddress) - if queryreturn != []: for row in queryreturn: fromLabel, = row + if fromLabel == '': + fromLabel = fromAddress # message row self.ui.tableWidgetInbox.insertRow(0) + # to newItem = QtGui.QTableWidgetItem(unicode(toLabel, 'utf-8')) newItem.setToolTip(unicode(toLabel, 'utf-8')) - # to newItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) if not read: @@ -885,24 +861,19 @@ class MyForm(QtGui.QMainWindow): newItem.setTextColor(QtGui.QColor(137, 04, 177)) # magenta if shared.safeConfigGetBoolean(str(toAddress), 'chan'): newItem.setTextColor(QtGui.QColor(216, 119, 0)) # orange - newItem.setIcon(avatarize(toAddress, True)) + newItem.setIcon(avatarize(toAddress)) self.ui.tableWidgetInbox.setItem(0, 0, newItem) # from - if fromLabel == '': - newItem = QtGui.QTableWidgetItem( - unicode(fromAddress, 'utf-8')) - newItem.setToolTip(unicode(fromAddress, 'utf-8')) - else: - newItem = QtGui.QTableWidgetItem(unicode(fromLabel, 'utf-8')) - newItem.setToolTip(unicode(fromLabel, 'utf-8')) + newItem = QtGui.QTableWidgetItem(unicode(fromLabel, 'utf-8')) + newItem.setToolTip(unicode(fromLabel, 'utf-8')) newItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) if not read: newItem.setFont(font) newItem.setData(Qt.UserRole, str(fromAddress)) - if checkIfChan & shared.safeConfigGetBoolean(str(toAddress), 'chan'): + if shared.safeConfigGetBoolean(str(fromAddress), 'chan'): newItem.setTextColor(QtGui.QColor(216, 119, 0)) # orange - newItem.setIcon(avatarize(fromAddress, True)) + newItem.setIcon(avatarize(fromAddress)) self.ui.tableWidgetInbox.setItem(0, 1, newItem) # subject newItem = QtGui.QTableWidgetItem(unicode(subject, 'utf-8')) @@ -1585,11 +1556,8 @@ class MyForm(QtGui.QMainWindow): if queryreturn != []: for row in queryreturn: fromLabel, = row - self.ui.tableWidgetInbox.item( - i, 1).setText(unicode(fromLabel, 'utf-8')) - self.ui.tableWidgetInbox.item( - i, 1).setIcon(avatarize(addressToLookup, True)) - else: + + if fromLabel == '': # It might be a broadcast message. We should check for that # label. queryreturn = sqlQuery( @@ -1598,46 +1566,38 @@ class MyForm(QtGui.QMainWindow): if queryreturn != []: for row in queryreturn: fromLabel, = row - self.ui.tableWidgetInbox.item( - i, 1).setText(unicode(fromLabel, 'utf-8')) - self.ui.tableWidgetInbox.item( - i, 1).setIcon(avatarize(addressToLookup, True)) - else: - # It might be a chan message. We should check for that - # label. - try: - fromLabel = shared.config.get(addressToLookup, 'label') - except: - fromLabel = '' - if fromLabel == '': - fromLabel = addressToLookup - self.ui.tableWidgetInbox.item( - i, 1).setText(unicode(fromLabel, 'utf-8')) - self.ui.tableWidgetInbox.item( - i, 1).setIcon(avatarize(addressToLookup, True)) - # Set the color according to whether it is the address of a mailing - # list or not. - if shared.safeConfigGetBoolean(addressToLookup, 'chan'): - self.ui.tableWidgetInbox.item(i, 1).setTextColor(QtGui.QColor(216, 119, 0)) # orange - else: - self.ui.tableWidgetInbox.item( - i, 1).setTextColor(QApplication.palette().text().color()) + if fromLabel == '': + # Message might be from an address we own like a chan address. Let's look for that label. + if shared.config.has_section(addressToLookup): + fromLabel = shared.config.get(addressToLookup, 'label') + if fromLabel == '': + fromLabel = addressToLookup + self.ui.tableWidgetInbox.item( + i, 1).setText(unicode(fromLabel, 'utf-8')) + self.ui.tableWidgetInbox.item( + i, 1).setIcon(avatarize(addressToLookup)) + # Set the color according to whether it is the address of a mailing + # list or not. + if shared.safeConfigGetBoolean(addressToLookup, 'chan'): + self.ui.tableWidgetInbox.item(i, 1).setTextColor(QtGui.QColor(216, 119, 0)) # orange + else: + self.ui.tableWidgetInbox.item( + i, 1).setTextColor(QApplication.palette().text().color()) def rerenderInboxToLabels(self): for i in range(self.ui.tableWidgetInbox.rowCount()): toAddress = str(self.ui.tableWidgetInbox.item( i, 0).data(Qt.UserRole).toPyObject()) - try: + # Message might be to an address we own like a chan address. Let's look for that label. + if shared.config.has_section(toAddress): toLabel = shared.config.get(toAddress, 'label') - except: - toLabel = '' - if toLabel == '': + else: toLabel = toAddress self.ui.tableWidgetInbox.item( i, 0).setText(unicode(toLabel, 'utf-8')) self.ui.tableWidgetInbox.item( - i, 0).setIcon(avatarize(toAddress, True)) + i, 0).setIcon(avatarize(toAddress)) # Set the color according to whether it is the address of a mailing # list, a chan or neither. if shared.safeConfigGetBoolean(toAddress, 'chan'): @@ -1652,16 +1612,15 @@ class MyForm(QtGui.QMainWindow): for i in range(self.ui.tableWidgetSent.rowCount()): fromAddress = str(self.ui.tableWidgetSent.item( i, 1).data(Qt.UserRole).toPyObject()) - try: + # Message might be from an address we own like a chan address. Let's look for that label. + if shared.config.has_section(fromAddress): fromLabel = shared.config.get(fromAddress, 'label') - except: - fromLabel = '' - if fromLabel == '': + else: fromLabel = fromAddress self.ui.tableWidgetSent.item( i, 1).setText(unicode(fromLabel, 'utf-8')) self.ui.tableWidgetSent.item( - i, 1).setIcon(avatarize(fromAddress, True)) + i, 1).setIcon(avatarize(fromAddress)) def rerenderSentToLabels(self): for i in range(self.ui.tableWidgetSent.rowCount()): @@ -1670,12 +1629,17 @@ class MyForm(QtGui.QMainWindow): toLabel = '' queryreturn = sqlQuery( '''select label from addressbook where address=?''', addressToLookup) - if queryreturn != []: for row in queryreturn: toLabel, = row - self.ui.tableWidgetSent.item( - i, 0).setText(unicode(toLabel, 'utf-8')) + + if toLabel == '': + # Message might be to an address we own like a chan address. Let's look for that label. + if shared.config.has_section(addressToLookup): + toLabel = shared.config.get(addressToLookup, 'label') + toLabel = addressToLookup + self.ui.tableWidgetSent.item( + i, 0).setText(unicode(toLabel, 'utf-8')) def rerenderAddressBook(self): self.ui.tableWidgetAddressBook.setRowCount(0) @@ -1684,6 +1648,7 @@ class MyForm(QtGui.QMainWindow): label, address = row self.ui.tableWidgetAddressBook.insertRow(0) newItem = QtGui.QTableWidgetItem(unicode(label, 'utf-8')) + newItem.setIcon(avatarize(address)) self.ui.tableWidgetAddressBook.setItem(0, 0, newItem) newItem = QtGui.QTableWidgetItem(address) newItem.setFlags( @@ -1706,7 +1671,6 @@ class MyForm(QtGui.QMainWindow): QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) if not enabled: newItem.setTextColor(QtGui.QColor(128, 128, 128)) - newItem.setIcon(identiconize(address)) self.ui.tableWidgetSubscriptions.setItem(0, 1, newItem) def click_pushButtonSend(self): @@ -1881,7 +1845,7 @@ class MyForm(QtGui.QMainWindow): isEnabled = shared.config.getboolean( addressInKeysFile, 'enabled') # I realize that this is poor programming practice but I don't care. It's easier for others to read. if isEnabled: - self.ui.comboBoxSendFrom.insertItem(0, avatarize(addressInKeysFile, True), unicode(shared.config.get( + self.ui.comboBoxSendFrom.insertItem(0, avatarize(addressInKeysFile), unicode(shared.config.get( addressInKeysFile, 'label'), 'utf-8'), addressInKeysFile) self.ui.comboBoxSendFrom.insertItem(0, '', '') if(self.ui.comboBoxSendFrom.count() == 2): @@ -1913,7 +1877,7 @@ class MyForm(QtGui.QMainWindow): newItem = QtGui.QTableWidgetItem(unicode(toLabel, 'utf-8')) newItem.setToolTip(unicode(toLabel, 'utf-8')) newItem.setData(Qt.UserRole, str(toAddress)) - newItem.setIcon(avatarize(toAddress, True)) + newItem.setIcon(avatarize(toAddress)) self.ui.tableWidgetSent.setItem(0, 0, newItem) if fromLabel == '': newItem = QtGui.QTableWidgetItem(unicode(fromAddress, 'utf-8')) @@ -1922,7 +1886,7 @@ class MyForm(QtGui.QMainWindow): newItem = QtGui.QTableWidgetItem(unicode(fromLabel, 'utf-8')) newItem.setToolTip(unicode(fromLabel, 'utf-8')) newItem.setData(Qt.UserRole, str(fromAddress)) - newItem.setIcon(avatarize(fromAddress, True)) + newItem.setIcon(avatarize(fromAddress)) self.ui.tableWidgetSent.setItem(0, 1, newItem) newItem = QtGui.QTableWidgetItem(unicode(subject, 'utf-8)')) newItem.setToolTip(unicode(subject, 'utf-8)')) @@ -1944,7 +1908,6 @@ class MyForm(QtGui.QMainWindow): def displayNewInboxMessage(self, inventoryHash, toAddress, fromAddress, subject, message): subject = shared.fixPotentiallyInvalidUTF8Data(subject) - #message = shared.fixPotentiallyInvalidUTF8Data(message) fromLabel = '' queryreturn = sqlQuery( '''select label from addressbook where address=?''', fromAddress) @@ -1981,7 +1944,7 @@ class MyForm(QtGui.QMainWindow): if shared.safeConfigGetBoolean(str(toAddress), 'chan'): newItem.setTextColor(QtGui.QColor(216, 119, 0)) # orange self.ui.tableWidgetInbox.insertRow(0) - newItem.setIcon(avatarize(toAddress, True)) + newItem.setIcon(avatarize(toAddress)) self.ui.tableWidgetInbox.setItem(0, 0, newItem) if fromLabel == '': @@ -1996,7 +1959,7 @@ class MyForm(QtGui.QMainWindow): self.notifierShow(unicode(_translate("MainWindow",'New Message').toUtf8(),'utf-8'), unicode(_translate("MainWindow",'From ').toUtf8(),'utf-8') + unicode(fromLabel, 'utf-8'), self.SOUND_KNOWN, unicode(fromLabel, 'utf-8')) newItem.setData(Qt.UserRole, str(fromAddress)) newItem.setFont(font) - newItem.setIcon(avatarize(fromAddress, True)) + newItem.setIcon(avatarize(fromAddress)) self.ui.tableWidgetInbox.setItem(0, 1, newItem) newItem = QtGui.QTableWidgetItem(unicode(subject, 'utf-8)')) newItem.setToolTip(unicode(subject, 'utf-8)')) @@ -2038,7 +2001,6 @@ class MyForm(QtGui.QMainWindow): newItem.setIcon(avatarize(address)) self.ui.tableWidgetAddressBook.setItem(0, 0, newItem) newItem = QtGui.QTableWidgetItem(address) - newItem.setIcon(identiconize(address)) newItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) self.ui.tableWidgetAddressBook.setItem(0, 1, newItem) @@ -2062,7 +2024,6 @@ class MyForm(QtGui.QMainWindow): newItem.setIcon(avatarize(address)) self.ui.tableWidgetSubscriptions.setItem(0,0,newItem) newItem = QtGui.QTableWidgetItem(address) - newItem.setIcon(identiconize(address)) newItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled ) self.ui.tableWidgetSubscriptions.setItem(0,1,newItem) self.ui.tableWidgetSubscriptions.setSortingEnabled(True) @@ -2101,7 +2062,6 @@ class MyForm(QtGui.QMainWindow): newItem.setIcon(avatarize(address)) self.ui.tableWidgetBlacklist.setItem(0, 0, newItem) newItem = QtGui.QTableWidgetItem(address) - newItem.setIcon(identiconize(address)) newItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) if not enabled: @@ -2135,19 +2095,13 @@ class MyForm(QtGui.QMainWindow): self.settingsDialogInstance.ui.checkBoxStartInTray.isChecked())) shared.config.set('bitmessagesettings', 'willinglysendtomobile', str( self.settingsDialogInstance.ui.checkBoxWillinglySendToMobile.isChecked())) + shared.config.set('bitmessagesettings', 'useidenticons', str( + self.settingsDialogInstance.ui.checkBoxUseIdenticons.isChecked())) lang_ind = int(self.settingsDialogInstance.ui.languageComboBox.currentIndex()) if not languages[lang_ind] == 'other': shared.config.set('bitmessagesettings', 'userlocale', languages[lang_ind]) - - curr_index = self.settingsDialogInstance.ui.comboBoxIdenticonStyle.currentIndex() - shared.config.set('bitmessagesettings', 'identicon', str(self.settingsDialogInstance.ui.comboBoxIdenticonStyle.itemData( - curr_index , Qt.UserRole).toString())) - shared.config.set('bitmessagesettings', 'identiconsuffix', str( - self.settingsDialogInstance.ui.lineEditIdenticonSuffix.text())) - shared.config.set('bitmessagesettings', 'avatars', str( - self.settingsDialogInstance.ui.checkBoxLoadAvatars.isChecked())) - + if int(shared.config.get('bitmessagesettings', 'port')) != int(self.settingsDialogInstance.ui.lineEditTCPPort.text()): if not shared.safeConfigGetBoolean('bitmessagesettings', 'dontconnect'): QMessageBox.about(self, _translate("MainWindow", "Restart"), _translate( @@ -2297,11 +2251,12 @@ class MyForm(QtGui.QMainWindow): self.NewBlacklistDialogInstance = NewSubscriptionDialog(self) if self.NewBlacklistDialogInstance.exec_(): if self.NewBlacklistDialogInstance.ui.labelSubscriptionAddressCheck.text() == _translate("MainWindow", "Address is valid."): + address = addBMIfNotPresent(str( + self.NewBlacklistDialogInstance.ui.lineEditSubscriptionAddress.text())) # First we must check to see if the address is already in the # address book. The user cannot add it again or else it will # cause problems when updating and deleting the entry. - t = (addBMIfNotPresent(str( - self.NewBlacklistDialogInstance.ui.lineEditSubscriptionAddress.text())),) + t = (address,) if shared.config.get('bitmessagesettings', 'blackwhitelist') == 'black': sql = '''select * from blacklist where address=?''' else: @@ -2312,15 +2267,14 @@ class MyForm(QtGui.QMainWindow): self.ui.tableWidgetBlacklist.insertRow(0) newItem = QtGui.QTableWidgetItem(unicode( self.NewBlacklistDialogInstance.ui.newsubscriptionlabel.text().toUtf8(), 'utf-8')) + newItem.setIcon(avatarize(address)) self.ui.tableWidgetBlacklist.setItem(0, 0, newItem) - newItem = QtGui.QTableWidgetItem(addBMIfNotPresent( - self.NewBlacklistDialogInstance.ui.lineEditSubscriptionAddress.text())) + newItem = QtGui.QTableWidgetItem(address) newItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) self.ui.tableWidgetBlacklist.setItem(0, 1, newItem) self.ui.tableWidgetBlacklist.setSortingEnabled(True) - t = (str(self.NewBlacklistDialogInstance.ui.newsubscriptionlabel.text().toUtf8()), addBMIfNotPresent( - str(self.NewBlacklistDialogInstance.ui.lineEditSubscriptionAddress.text())), True) + t = (str(self.NewBlacklistDialogInstance.ui.newsubscriptionlabel.text().toUtf8()), address, True) if shared.config.get('bitmessagesettings', 'blackwhitelist') == 'black': sql = '''INSERT INTO blacklist VALUES (?,?,?)''' else: @@ -2529,6 +2483,7 @@ class MyForm(QtGui.QMainWindow): newItem = QtGui.QTableWidgetItem( '--New entry. Change label in Address Book.--') self.ui.tableWidgetAddressBook.setItem(0, 0, newItem) + newItem.setIcon(avatarize(addressAtCurrentInboxRow)) newItem = QtGui.QTableWidgetItem(addressAtCurrentInboxRow) newItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) @@ -2880,10 +2835,11 @@ class MyForm(QtGui.QMainWindow): def on_action_SetAvatar(self, thisTableWidget): # thisTableWidget = self.ui.tableWidgetYourIdentities + if not os.path.exists(shared.appdata + 'avatars/'): + os.makedirs(shared.appdata + 'avatars/') currentRow = thisTableWidget.currentRow() addressAtCurrentRow = thisTableWidget.item( currentRow, 1).text() - import hashlib hash = hashlib.md5(addBMIfNotPresent(addressAtCurrentRow)).hexdigest() extensions = ['PNG', 'GIF', 'JPG', 'JPEG', 'SVG', 'BMP', 'MNG', 'PBM', 'PGM', 'PPM', 'TIFF', 'XBM', 'XPM', 'TGA'] # http://pyqt.sourceforge.net/Docs/PyQt4/qimagereader.html#supportedImageFormats @@ -2938,7 +2894,6 @@ class MyForm(QtGui.QMainWindow): # set the icon thisTableWidget.item( currentRow, 0).setIcon(avatarize(addressAtCurrentRow)) - shared.reloadBroadcastSendersForWhichImWatching() self.rerenderSubscriptions() self.rerenderComboBoxSendFrom() self.rerenderInboxFromLabels() @@ -3001,6 +2956,7 @@ class MyForm(QtGui.QMainWindow): if queryreturn != []: for row in queryreturn: messageText, = row + messageText = shared.fixPotentiallyInvalidUTF8Data(messageText) messageText = unicode(messageText, 'utf-8)') if len(messageText) > 30000: messageText = ( @@ -3090,7 +3046,6 @@ class MyForm(QtGui.QMainWindow): self.ui.tableWidgetYourIdentities.setItem( 0, 0, newItem) newItem = QtGui.QTableWidgetItem(address) - newItem.setIcon(identiconize(address)) newItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) if shared.safeConfigGetBoolean(address, 'chan'): @@ -3166,6 +3121,8 @@ class settingsDialog(QtGui.QDialog): shared.config.getboolean('bitmessagesettings', 'startintray')) self.ui.checkBoxWillinglySendToMobile.setChecked( shared.safeConfigGetBoolean('bitmessagesettings', 'willinglysendtomobile')) + self.ui.checkBoxUseIdenticons.setChecked( + shared.safeConfigGetBoolean('bitmessagesettings', 'useidenticons')) global languages languages = ['system','en','eo','fr','de','es','ru','en_pirate','other'] @@ -3176,18 +3133,6 @@ class settingsDialog(QtGui.QDialog): curr_index = languages.index('other') self.ui.languageComboBox.setCurrentIndex(curr_index) - self.ui.comboBoxIdenticonStyle.addItem(_translate("settingsDialog", "None"), "none") - self.ui.comboBoxIdenticonStyle.addItem(QIcon(":/newPrefix/images/qidenticon.png"), _translate("settingsDialog", "QIdenticon"), "qidenticon") - self.ui.comboBoxIdenticonStyle.addItem(QIcon(":/newPrefix/images/qidenticon_x.png"), _translate("settingsDialog", "QIdenticon (transparent)"), "qidenticon_x") - self.ui.comboBoxIdenticonStyle.addItem(QIcon(":/newPrefix/images/qidenticon_two.png"), _translate("settingsDialog", "QIdenticon bicolored"), "qidenticon_two") - self.ui.comboBoxIdenticonStyle.addItem(QIcon(":/newPrefix/images/qidenticon_two_x.png"), _translate("settingsDialog", "QIdenticon bicolored (transparent)"), "qidenticon_two_x") - curr_index = self.ui.comboBoxIdenticonStyle.findData(str(shared.config.get('bitmessagesettings', 'identicon')), Qt.UserRole) - self.ui.comboBoxIdenticonStyle.setCurrentIndex(curr_index) - self.ui.lineEditIdenticonSuffix.setText( - str(shared.config.get('bitmessagesettings', 'identiconsuffix'))) - self.ui.checkBoxLoadAvatars.setChecked( - shared.safeConfigGetBoolean('bitmessagesettings', 'avatars')) - if shared.appdata == '': self.ui.checkBoxPortableMode.setChecked(True) if 'darwin' in sys.platform: @@ -3199,8 +3144,6 @@ class settingsDialog(QtGui.QDialog): elif 'linux' in sys.platform: self.ui.checkBoxStartOnLogon.setDisabled(True) self.ui.checkBoxMinimizeToTray.setDisabled(True) - self.ui.labelSettingsNote.setText(_translate( - "MainWindow", "Options have been disabled because they either aren\'t applicable or because they haven\'t yet been implemented for your operating system.")) # On the Network settings tab: self.ui.lineEditTCPPort.setText(str( shared.config.get('bitmessagesettings', 'port'))) diff --git a/src/bitmessageqt/settings.py b/src/bitmessageqt/settings.py index 205f7799..152ab0b7 100644 --- a/src/bitmessageqt/settings.py +++ b/src/bitmessageqt/settings.py @@ -2,8 +2,8 @@ # Form implementation generated from reading ui file 'settings.ui' # -# Created: Sat Sep 21 16:16:22 2013 -# by: PyQt4 UI code generator 4.10.2 +# Created: Fri Nov 1 19:14:45 2013 +# by: PyQt4 UI code generator 4.10 # # WARNING! All changes made in this file will be lost! @@ -26,7 +26,7 @@ except AttributeError: class Ui_settingsDialog(object): def setupUi(self, settingsDialog): settingsDialog.setObjectName(_fromUtf8("settingsDialog")) - settingsDialog.resize(600, 407) + settingsDialog.resize(646, 473) self.gridLayout = QtGui.QGridLayout(settingsDialog) self.gridLayout.setObjectName(_fromUtf8("gridLayout")) self.buttonBox = QtGui.QDialogButtonBox(settingsDialog) @@ -39,12 +39,43 @@ class Ui_settingsDialog(object): self.tabUserInterface = QtGui.QWidget() self.tabUserInterface.setEnabled(True) self.tabUserInterface.setObjectName(_fromUtf8("tabUserInterface")) - self.gridLayout_5 = QtGui.QGridLayout(self.tabUserInterface) - self.gridLayout_5.setObjectName(_fromUtf8("gridLayout_5")) + self.formLayout = QtGui.QFormLayout(self.tabUserInterface) + self.formLayout.setObjectName(_fromUtf8("formLayout")) + self.checkBoxStartOnLogon = QtGui.QCheckBox(self.tabUserInterface) + self.checkBoxStartOnLogon.setObjectName(_fromUtf8("checkBoxStartOnLogon")) + self.formLayout.setWidget(0, QtGui.QFormLayout.LabelRole, self.checkBoxStartOnLogon) + self.checkBoxStartInTray = QtGui.QCheckBox(self.tabUserInterface) + self.checkBoxStartInTray.setObjectName(_fromUtf8("checkBoxStartInTray")) + self.formLayout.setWidget(1, QtGui.QFormLayout.SpanningRole, self.checkBoxStartInTray) + self.checkBoxMinimizeToTray = QtGui.QCheckBox(self.tabUserInterface) + self.checkBoxMinimizeToTray.setChecked(True) + self.checkBoxMinimizeToTray.setObjectName(_fromUtf8("checkBoxMinimizeToTray")) + self.formLayout.setWidget(2, QtGui.QFormLayout.LabelRole, self.checkBoxMinimizeToTray) + self.checkBoxShowTrayNotifications = QtGui.QCheckBox(self.tabUserInterface) + self.checkBoxShowTrayNotifications.setObjectName(_fromUtf8("checkBoxShowTrayNotifications")) + self.formLayout.setWidget(3, QtGui.QFormLayout.LabelRole, self.checkBoxShowTrayNotifications) + self.checkBoxPortableMode = QtGui.QCheckBox(self.tabUserInterface) + self.checkBoxPortableMode.setObjectName(_fromUtf8("checkBoxPortableMode")) + self.formLayout.setWidget(4, QtGui.QFormLayout.LabelRole, self.checkBoxPortableMode) + self.PortableModeDescription = QtGui.QLabel(self.tabUserInterface) + sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) + sizePolicy.setHorizontalStretch(0) + sizePolicy.setVerticalStretch(0) + sizePolicy.setHeightForWidth(self.PortableModeDescription.sizePolicy().hasHeightForWidth()) + self.PortableModeDescription.setSizePolicy(sizePolicy) + self.PortableModeDescription.setWordWrap(True) + self.PortableModeDescription.setObjectName(_fromUtf8("PortableModeDescription")) + self.formLayout.setWidget(5, QtGui.QFormLayout.SpanningRole, self.PortableModeDescription) + self.checkBoxWillinglySendToMobile = QtGui.QCheckBox(self.tabUserInterface) + self.checkBoxWillinglySendToMobile.setObjectName(_fromUtf8("checkBoxWillinglySendToMobile")) + self.formLayout.setWidget(6, QtGui.QFormLayout.SpanningRole, self.checkBoxWillinglySendToMobile) + self.checkBoxUseIdenticons = QtGui.QCheckBox(self.tabUserInterface) + self.checkBoxUseIdenticons.setObjectName(_fromUtf8("checkBoxUseIdenticons")) + self.formLayout.setWidget(7, QtGui.QFormLayout.LabelRole, self.checkBoxUseIdenticons) self.groupBox = QtGui.QGroupBox(self.tabUserInterface) self.groupBox.setObjectName(_fromUtf8("groupBox")) - self.horizontalLayout_2 = QtGui.QHBoxLayout(self.groupBox) - self.horizontalLayout_2.setObjectName(_fromUtf8("horizontalLayout_2")) + self.formLayout_2 = QtGui.QFormLayout(self.groupBox) + self.formLayout_2.setObjectName(_fromUtf8("formLayout_2")) self.languageComboBox = QtGui.QComboBox(self.groupBox) self.languageComboBox.setMinimumSize(QtCore.QSize(100, 0)) self.languageComboBox.setObjectName(_fromUtf8("languageComboBox")) @@ -57,63 +88,8 @@ class Ui_settingsDialog(object): self.languageComboBox.addItem(_fromUtf8("")) self.languageComboBox.addItem(_fromUtf8("")) self.languageComboBox.addItem(_fromUtf8("")) - self.horizontalLayout_2.addWidget(self.languageComboBox) - self.gridLayout_5.addWidget(self.groupBox, 7, 1, 4, 1) - self.checkBoxMinimizeToTray = QtGui.QCheckBox(self.tabUserInterface) - self.checkBoxMinimizeToTray.setChecked(True) - self.checkBoxMinimizeToTray.setObjectName(_fromUtf8("checkBoxMinimizeToTray")) - self.gridLayout_5.addWidget(self.checkBoxMinimizeToTray, 2, 0, 1, 1) - self.checkBoxStartOnLogon = QtGui.QCheckBox(self.tabUserInterface) - self.checkBoxStartOnLogon.setObjectName(_fromUtf8("checkBoxStartOnLogon")) - self.gridLayout_5.addWidget(self.checkBoxStartOnLogon, 0, 0, 1, 1) - self.checkBoxShowTrayNotifications = QtGui.QCheckBox(self.tabUserInterface) - self.checkBoxShowTrayNotifications.setObjectName(_fromUtf8("checkBoxShowTrayNotifications")) - self.gridLayout_5.addWidget(self.checkBoxShowTrayNotifications, 3, 0, 1, 1) - self.checkBoxPortableMode = QtGui.QCheckBox(self.tabUserInterface) - self.checkBoxPortableMode.setObjectName(_fromUtf8("checkBoxPortableMode")) - self.gridLayout_5.addWidget(self.checkBoxPortableMode, 4, 0, 1, 1) - self.checkBoxStartInTray = QtGui.QCheckBox(self.tabUserInterface) - self.checkBoxStartInTray.setObjectName(_fromUtf8("checkBoxStartInTray")) - self.gridLayout_5.addWidget(self.checkBoxStartInTray, 1, 0, 1, 1) - self.PortableModeDescription = QtGui.QLabel(self.tabUserInterface) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Fixed) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.PortableModeDescription.sizePolicy().hasHeightForWidth()) - self.PortableModeDescription.setSizePolicy(sizePolicy) - self.PortableModeDescription.setWordWrap(True) - self.PortableModeDescription.setObjectName(_fromUtf8("PortableModeDescription")) - self.gridLayout_5.addWidget(self.PortableModeDescription, 5, 0, 1, 2) - self.checkBoxWillinglySendToMobile = QtGui.QCheckBox(self.tabUserInterface) - self.checkBoxWillinglySendToMobile.setObjectName(_fromUtf8("checkBoxWillinglySendToMobile")) - self.gridLayout_5.addWidget(self.checkBoxWillinglySendToMobile, 6, 0, 1, 2) - self.groupBox_3 = QtGui.QGroupBox(self.tabUserInterface) - self.groupBox_3.setAlignment(QtCore.Qt.AlignLeading|QtCore.Qt.AlignLeft|QtCore.Qt.AlignTop) - self.groupBox_3.setFlat(False) - self.groupBox_3.setCheckable(False) - self.groupBox_3.setObjectName(_fromUtf8("groupBox_3")) - self.gridLayout_9 = QtGui.QGridLayout(self.groupBox_3) - self.gridLayout_9.setObjectName(_fromUtf8("gridLayout_9")) - self.checkBoxLoadAvatars = QtGui.QCheckBox(self.groupBox_3) - self.checkBoxLoadAvatars.setObjectName(_fromUtf8("checkBoxLoadAvatars")) - self.gridLayout_9.addWidget(self.checkBoxLoadAvatars, 1, 0, 1, 1) - self.lineEditIdenticonSuffix = QtGui.QLineEdit(self.groupBox_3) - self.lineEditIdenticonSuffix.setObjectName(_fromUtf8("lineEditIdenticonSuffix")) - self.gridLayout_9.addWidget(self.lineEditIdenticonSuffix, 1, 1, 1, 1) - self.comboBoxIdenticonStyle = QtGui.QComboBox(self.groupBox_3) - sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) - sizePolicy.setHorizontalStretch(0) - sizePolicy.setVerticalStretch(0) - sizePolicy.setHeightForWidth(self.comboBoxIdenticonStyle.sizePolicy().hasHeightForWidth()) - self.comboBoxIdenticonStyle.setSizePolicy(sizePolicy) - self.comboBoxIdenticonStyle.setIconSize(QtCore.QSize(24, 24)) - self.comboBoxIdenticonStyle.setObjectName(_fromUtf8("comboBoxIdenticonStyle")) - self.gridLayout_9.addWidget(self.comboBoxIdenticonStyle, 0, 0, 1, 2) - self.gridLayout_5.addWidget(self.groupBox_3, 7, 0, 5, 1) - spacerItem = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) - self.gridLayout_5.addItem(spacerItem, 12, 0, 1, 1) - spacerItem1 = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) - self.gridLayout_5.addItem(spacerItem1, 11, 1, 2, 1) + self.formLayout_2.setWidget(0, QtGui.QFormLayout.LabelRole, self.languageComboBox) + self.formLayout.setWidget(8, QtGui.QFormLayout.FieldRole, self.groupBox) self.tabWidgetSettings.addTab(self.tabUserInterface, _fromUtf8("")) self.tabNetworkSettings = QtGui.QWidget() self.tabNetworkSettings.setObjectName(_fromUtf8("tabNetworkSettings")) @@ -123,8 +99,8 @@ class Ui_settingsDialog(object): self.groupBox1.setObjectName(_fromUtf8("groupBox1")) self.gridLayout_3 = QtGui.QGridLayout(self.groupBox1) self.gridLayout_3.setObjectName(_fromUtf8("gridLayout_3")) - spacerItem2 = QtGui.QSpacerItem(125, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) - self.gridLayout_3.addItem(spacerItem2, 0, 0, 1, 1) + spacerItem = QtGui.QSpacerItem(125, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) + self.gridLayout_3.addItem(spacerItem, 0, 0, 1, 1) self.label = QtGui.QLabel(self.groupBox1) self.label.setObjectName(_fromUtf8("label")) self.gridLayout_3.addWidget(self.label, 0, 1, 1, 1) @@ -181,8 +157,8 @@ class Ui_settingsDialog(object): self.comboBoxProxyType.addItem(_fromUtf8("")) self.gridLayout_2.addWidget(self.comboBoxProxyType, 0, 1, 1, 1) self.gridLayout_4.addWidget(self.groupBox_2, 1, 0, 1, 1) - spacerItem3 = QtGui.QSpacerItem(20, 70, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) - self.gridLayout_4.addItem(spacerItem3, 2, 0, 1, 1) + spacerItem1 = QtGui.QSpacerItem(20, 70, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) + self.gridLayout_4.addItem(spacerItem1, 2, 0, 1, 1) self.tabWidgetSettings.addTab(self.tabNetworkSettings, _fromUtf8("")) self.tab = QtGui.QWidget() self.tab.setObjectName(_fromUtf8("tab")) @@ -192,8 +168,8 @@ class Ui_settingsDialog(object): self.label_8.setWordWrap(True) self.label_8.setObjectName(_fromUtf8("label_8")) self.gridLayout_6.addWidget(self.label_8, 0, 0, 1, 3) - spacerItem4 = QtGui.QSpacerItem(203, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) - self.gridLayout_6.addItem(spacerItem4, 1, 0, 1, 1) + spacerItem2 = QtGui.QSpacerItem(203, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) + self.gridLayout_6.addItem(spacerItem2, 1, 0, 1, 1) self.label_9 = QtGui.QLabel(self.tab) self.label_9.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.label_9.setObjectName(_fromUtf8("label_9")) @@ -207,8 +183,8 @@ class Ui_settingsDialog(object): self.lineEditTotalDifficulty.setMaximumSize(QtCore.QSize(70, 16777215)) self.lineEditTotalDifficulty.setObjectName(_fromUtf8("lineEditTotalDifficulty")) self.gridLayout_6.addWidget(self.lineEditTotalDifficulty, 1, 2, 1, 1) - spacerItem5 = QtGui.QSpacerItem(203, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) - self.gridLayout_6.addItem(spacerItem5, 3, 0, 1, 1) + spacerItem3 = QtGui.QSpacerItem(203, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) + self.gridLayout_6.addItem(spacerItem3, 3, 0, 1, 1) self.label_11 = QtGui.QLabel(self.tab) self.label_11.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.label_11.setObjectName(_fromUtf8("label_11")) @@ -239,8 +215,8 @@ class Ui_settingsDialog(object): self.label_15.setWordWrap(True) self.label_15.setObjectName(_fromUtf8("label_15")) self.gridLayout_7.addWidget(self.label_15, 0, 0, 1, 3) - spacerItem6 = QtGui.QSpacerItem(102, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) - self.gridLayout_7.addItem(spacerItem6, 1, 0, 1, 1) + spacerItem4 = QtGui.QSpacerItem(102, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) + self.gridLayout_7.addItem(spacerItem4, 1, 0, 1, 1) self.label_13 = QtGui.QLabel(self.tab_2) self.label_13.setLayoutDirection(QtCore.Qt.LeftToRight) self.label_13.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) @@ -255,8 +231,8 @@ class Ui_settingsDialog(object): self.lineEditMaxAcceptableTotalDifficulty.setMaximumSize(QtCore.QSize(70, 16777215)) self.lineEditMaxAcceptableTotalDifficulty.setObjectName(_fromUtf8("lineEditMaxAcceptableTotalDifficulty")) self.gridLayout_7.addWidget(self.lineEditMaxAcceptableTotalDifficulty, 1, 2, 1, 1) - spacerItem7 = QtGui.QSpacerItem(102, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) - self.gridLayout_7.addItem(spacerItem7, 2, 0, 1, 1) + spacerItem5 = QtGui.QSpacerItem(102, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) + self.gridLayout_7.addItem(spacerItem5, 2, 0, 1, 1) self.label_14 = QtGui.QLabel(self.tab_2) self.label_14.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.label_14.setObjectName(_fromUtf8("label_14")) @@ -270,15 +246,15 @@ class Ui_settingsDialog(object): self.lineEditMaxAcceptableSmallMessageDifficulty.setMaximumSize(QtCore.QSize(70, 16777215)) self.lineEditMaxAcceptableSmallMessageDifficulty.setObjectName(_fromUtf8("lineEditMaxAcceptableSmallMessageDifficulty")) self.gridLayout_7.addWidget(self.lineEditMaxAcceptableSmallMessageDifficulty, 2, 2, 1, 1) - spacerItem8 = QtGui.QSpacerItem(20, 147, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) - self.gridLayout_7.addItem(spacerItem8, 3, 1, 1, 1) + spacerItem6 = QtGui.QSpacerItem(20, 147, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) + self.gridLayout_7.addItem(spacerItem6, 3, 1, 1, 1) self.tabWidgetSettings.addTab(self.tab_2, _fromUtf8("")) self.tabNamecoin = QtGui.QWidget() self.tabNamecoin.setObjectName(_fromUtf8("tabNamecoin")) self.gridLayout_8 = QtGui.QGridLayout(self.tabNamecoin) self.gridLayout_8.setObjectName(_fromUtf8("gridLayout_8")) - spacerItem9 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) - self.gridLayout_8.addItem(spacerItem9, 2, 0, 1, 1) + spacerItem7 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) + self.gridLayout_8.addItem(spacerItem7, 2, 0, 1, 1) self.label_16 = QtGui.QLabel(self.tabNamecoin) self.label_16.setWordWrap(True) self.label_16.setObjectName(_fromUtf8("label_16")) @@ -290,10 +266,10 @@ class Ui_settingsDialog(object): self.lineEditNamecoinHost = QtGui.QLineEdit(self.tabNamecoin) self.lineEditNamecoinHost.setObjectName(_fromUtf8("lineEditNamecoinHost")) self.gridLayout_8.addWidget(self.lineEditNamecoinHost, 2, 2, 1, 1) - spacerItem10 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) - self.gridLayout_8.addItem(spacerItem10, 3, 0, 1, 1) - spacerItem11 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) - self.gridLayout_8.addItem(spacerItem11, 4, 0, 1, 1) + spacerItem8 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) + self.gridLayout_8.addItem(spacerItem8, 3, 0, 1, 1) + spacerItem9 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) + self.gridLayout_8.addItem(spacerItem9, 4, 0, 1, 1) self.label_18 = QtGui.QLabel(self.tabNamecoin) self.label_18.setEnabled(True) self.label_18.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) @@ -302,8 +278,8 @@ class Ui_settingsDialog(object): self.lineEditNamecoinPort = QtGui.QLineEdit(self.tabNamecoin) self.lineEditNamecoinPort.setObjectName(_fromUtf8("lineEditNamecoinPort")) self.gridLayout_8.addWidget(self.lineEditNamecoinPort, 3, 2, 1, 1) - spacerItem12 = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) - self.gridLayout_8.addItem(spacerItem12, 8, 1, 1, 1) + spacerItem10 = QtGui.QSpacerItem(20, 40, QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Expanding) + self.gridLayout_8.addItem(spacerItem10, 8, 1, 1, 1) self.labelNamecoinUser = QtGui.QLabel(self.tabNamecoin) self.labelNamecoinUser.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.labelNamecoinUser.setObjectName(_fromUtf8("labelNamecoinUser")) @@ -311,8 +287,8 @@ class Ui_settingsDialog(object): self.lineEditNamecoinUser = QtGui.QLineEdit(self.tabNamecoin) self.lineEditNamecoinUser.setObjectName(_fromUtf8("lineEditNamecoinUser")) self.gridLayout_8.addWidget(self.lineEditNamecoinUser, 4, 2, 1, 1) - spacerItem13 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) - self.gridLayout_8.addItem(spacerItem13, 5, 0, 1, 1) + spacerItem11 = QtGui.QSpacerItem(40, 20, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum) + self.gridLayout_8.addItem(spacerItem11, 5, 0, 1, 1) self.labelNamecoinPassword = QtGui.QLabel(self.tabNamecoin) self.labelNamecoinPassword.setAlignment(QtCore.Qt.AlignRight|QtCore.Qt.AlignTrailing|QtCore.Qt.AlignVCenter) self.labelNamecoinPassword.setObjectName(_fromUtf8("labelNamecoinPassword")) @@ -354,8 +330,7 @@ class Ui_settingsDialog(object): settingsDialog.setTabOrder(self.tabWidgetSettings, self.checkBoxStartOnLogon) settingsDialog.setTabOrder(self.checkBoxStartOnLogon, self.checkBoxStartInTray) settingsDialog.setTabOrder(self.checkBoxStartInTray, self.checkBoxMinimizeToTray) - settingsDialog.setTabOrder(self.checkBoxMinimizeToTray, self.checkBoxShowTrayNotifications) - settingsDialog.setTabOrder(self.checkBoxShowTrayNotifications, self.lineEditTCPPort) + settingsDialog.setTabOrder(self.checkBoxMinimizeToTray, self.lineEditTCPPort) settingsDialog.setTabOrder(self.lineEditTCPPort, self.comboBoxProxyType) settingsDialog.setTabOrder(self.comboBoxProxyType, self.lineEditSocksHostname) settingsDialog.setTabOrder(self.lineEditSocksHostname, self.lineEditSocksPort) @@ -367,6 +342,14 @@ class Ui_settingsDialog(object): def retranslateUi(self, settingsDialog): settingsDialog.setWindowTitle(_translate("settingsDialog", "Settings", None)) + self.checkBoxStartOnLogon.setText(_translate("settingsDialog", "Start Bitmessage on user login", None)) + self.checkBoxStartInTray.setText(_translate("settingsDialog", "Start Bitmessage in the tray (don\'t show main window)", None)) + self.checkBoxMinimizeToTray.setText(_translate("settingsDialog", "Minimize to tray", None)) + self.checkBoxShowTrayNotifications.setText(_translate("settingsDialog", "Show notification when message received", None)) + self.checkBoxPortableMode.setText(_translate("settingsDialog", "Run in Portable Mode", None)) + self.PortableModeDescription.setText(_translate("settingsDialog", "In Portable Mode, messages and config files are stored in the same directory as the program rather than the normal application-data folder. This makes it convenient to run Bitmessage from a USB thumb drive.", None)) + self.checkBoxWillinglySendToMobile.setText(_translate("settingsDialog", "Willingly include unencrypted destination address when sending to a mobile device", None)) + self.checkBoxUseIdenticons.setText(_translate("settingsDialog", "Use Identicons", None)) self.groupBox.setTitle(_translate("settingsDialog", "Interface Language", None)) self.languageComboBox.setItemText(0, _translate("settingsDialog", "System Settings", "system")) self.languageComboBox.setItemText(1, _translate("settingsDialog", "English", "en")) @@ -377,16 +360,6 @@ class Ui_settingsDialog(object): self.languageComboBox.setItemText(6, _translate("settingsDialog", "русский язык", "ru")) self.languageComboBox.setItemText(7, _translate("settingsDialog", "Pirate English", "en_pirate")) self.languageComboBox.setItemText(8, _translate("settingsDialog", "Other (set in keys.dat)", "other")) - self.checkBoxMinimizeToTray.setText(_translate("settingsDialog", "Minimize to tray", None)) - self.checkBoxStartOnLogon.setText(_translate("settingsDialog", "Start Bitmessage on user login", None)) - self.checkBoxShowTrayNotifications.setText(_translate("settingsDialog", "Show notification when message received", None)) - self.checkBoxPortableMode.setText(_translate("settingsDialog", "Run in Portable Mode", None)) - self.checkBoxStartInTray.setText(_translate("settingsDialog", "Start Bitmessage in the tray (don\'t show main window)", None)) - self.PortableModeDescription.setText(_translate("settingsDialog", "In Portable Mode, messages and config files are stored in the same directory as the program rather than the normal application-data folder. This makes it convenient to run Bitmessage from a USB thumb drive.", None)) - self.checkBoxWillinglySendToMobile.setText(_translate("settingsDialog", "Willingly include unencrypted destination address when sending to a mobile device", None)) - self.groupBox_3.setTitle(_translate("settingsDialog", "Identicons (with example image)", None)) - self.checkBoxLoadAvatars.setText(_translate("settingsDialog", "Load avatar images", None)) - self.lineEditIdenticonSuffix.setToolTip(_translate("settingsDialog", "

The content of this text field will be appended to the BM-address before creating the hash for the identicons. By default it is filled with a random string to make the identicons in your client unique, otherwise the identicon could be an attack vector if an adversary creates an address resulting in a similar identicon. If you keep this string (or any other random or non-random string) you will be able to keep the same identicons.

", None)) self.tabWidgetSettings.setTabText(self.tabWidgetSettings.indexOf(self.tabUserInterface), _translate("settingsDialog", "User Interface", None)) self.groupBox1.setTitle(_translate("settingsDialog", "Listening port", None)) self.label.setText(_translate("settingsDialog", "Listen for connections on port:", None)) @@ -424,13 +397,3 @@ class Ui_settingsDialog(object): self.tabWidgetSettings.setTabText(self.tabWidgetSettings.indexOf(self.tabNamecoin), _translate("settingsDialog", "Namecoin integration", None)) import bitmessage_icons_rc - -if __name__ == "__main__": - import sys - app = QtGui.QApplication(sys.argv) - settingsDialog = QtGui.QDialog() - ui = Ui_settingsDialog() - ui.setupUi(settingsDialog) - settingsDialog.show() - sys.exit(app.exec_()) - diff --git a/src/bitmessageqt/settings.ui b/src/bitmessageqt/settings.ui index cce28f96..b7d7ae22 100644 --- a/src/bitmessageqt/settings.ui +++ b/src/bitmessageqt/settings.ui @@ -6,8 +6,8 @@ 0 0 - 600 - 407 + 646 + 473
@@ -36,14 +36,82 @@ User Interface - - + + + + + Start Bitmessage on user login + + + + + + + Start Bitmessage in the tray (don't show main window) + + + + + + + Minimize to tray + + + true + + + + + + + Show notification when message received + + + + + + + Run in Portable Mode + + + + + + + + 0 + 0 + + + + In Portable Mode, messages and config files are stored in the same directory as the program rather than the normal application-data folder. This makes it convenient to run Bitmessage from a USB thumb drive. + + + true + + + + + + + Willingly include unencrypted destination address when sending to a mobile device + + + + + + + Use Identicons + + + + Interface Language - - + + @@ -101,141 +169,6 @@ - - - - Minimize to tray - - - true - - - - - - - Start Bitmessage on user login - - - - - - - Show notification when message received - - - - - - - Run in Portable Mode - - - - - - - Start Bitmessage in the tray (don't show main window) - - - - - - - - 0 - 0 - - - - In Portable Mode, messages and config files are stored in the same directory as the program rather than the normal application-data folder. This makes it convenient to run Bitmessage from a USB thumb drive. - - - true - - - - - - - Willingly include unencrypted destination address when sending to a mobile device - - - - - - - Identicons (with example image) - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - false - - - false - - - - - - Load avatar images - - - - - - - <html><head/><body><p>The content of this text field will be appended to the BM-address before creating the hash for the identicons. By default it is filled with a random string to make the identicons in your client unique, otherwise the identicon could be an attack vector if an adversary creates an address resulting in a similar identicon. If you keep this string (or any other random or non-random string) you will be able to keep the same identicons.</p></body></html> - - - - - - - - 0 - 0 - - - - - 24 - 24 - - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - -
@@ -818,7 +751,6 @@ checkBoxStartOnLogon checkBoxStartInTray checkBoxMinimizeToTray - checkBoxShowTrayNotifications lineEditTCPPort comboBoxProxyType lineEditSocksHostname diff --git a/src/class_sqlThread.py b/src/class_sqlThread.py index e09385fd..24679acb 100644 --- a/src/class_sqlThread.py +++ b/src/class_sqlThread.py @@ -7,6 +7,8 @@ import sys import os from debug import logger from namecoin import ensureNamecoinOptions +import random +import string import tr#anslate # This thread exists because SQLITE3 is so un-threadsafe that we must @@ -242,13 +244,6 @@ class sqlThread(threading.Thread): if not shared.config.has_option('bitmessagesettings', 'userlocale'): shared.config.set('bitmessagesettings', 'userlocale', 'system') - if not shared.config.has_option('bitmessagesettings', 'identicon'): - shared.config.set('bitmessagesettings', 'identicon', 'None') - if not shared.config.has_option('bitmessagesettings', 'identiconsuffix'): - import random, string - shared.config.set('bitmessagesettings', 'identiconsuffix', ''.join(random.choice("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz") for x in range(12))) # a twelve character pseudo-password to salt the identicons - if not shared.config.has_option('bitmessagesettings', 'avatars'): - shared.config.set('bitmessagesettings', 'avatars', 'false') if not shared.config.has_option('bitmessagesettings', 'sendoutgoingconnections'): shared.config.set('bitmessagesettings', 'sendoutgoingconnections', 'True') @@ -275,6 +270,13 @@ class sqlThread(threading.Thread): item = '''update settings set value=? WHERE key='version';''' parameters = (5,) self.cur.execute(item, parameters) + + if not shared.config.has_option('bitmessagesettings', 'useidenticons'): + shared.config.set('bitmessagesettings', 'useidenticons', 'True') + if not shared.config.has_option('bitmessagesettings', 'identiconsuffix'): # acts as a salt + shared.config.set('bitmessagesettings', 'identiconsuffix', ''.join(random.choice("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz") for x in range(12))) # a twelve character pseudo-password to salt the identicons + with open(shared.appdata + 'keys.dat', 'wb') as configfile: + shared.config.write(configfile) # Are you hoping to add a new option to the keys.dat file of existing # Bitmessage users? Add it right above this line! diff --git a/src/helper_startup.py b/src/helper_startup.py index 17aafd55..164348eb 100644 --- a/src/helper_startup.py +++ b/src/helper_startup.py @@ -3,6 +3,8 @@ import ConfigParser import sys import os import locale +import random +import string from namecoin import ensureNamecoinOptions @@ -84,10 +86,8 @@ def loadConfig(): 'bitmessagesettings', 'maxacceptablepayloadlengthextrabytes', '0') shared.config.set('bitmessagesettings', 'dontconnect', 'true') shared.config.set('bitmessagesettings', 'userlocale', 'system') - shared.config.set('bitmessagesettings', 'identicon', 'None') - import random, string + shared.config.set('bitmessagesettings', 'useidenticons', 'True') shared.config.set('bitmessagesettings', 'identiconsuffix', ''.join(random.choice("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz") for x in range(12))) # a twelve character pseudo-password to salt the identicons - shared.config.set('bitmessagesettings', 'avatars', 'false') # Are you hoping to add a new option to the keys.dat file? You're in # the right place for adding it to users who install the software for From a9898feab8146917d3647fb3ec657b093c111c49 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Fri, 1 Nov 2013 19:28:44 -0400 Subject: [PATCH 17/22] removed images/can-icon-24px_2.png --- src/images/can-icon-24px_2.png | Bin 1852 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 src/images/can-icon-24px_2.png diff --git a/src/images/can-icon-24px_2.png b/src/images/can-icon-24px_2.png deleted file mode 100644 index 30f7313eb378b69c4e2f6cb66264c2ae678059e1..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1852 zcmV-C2gCS@P)M^!E?E!Z`=4*Xy`ht_>fhjLl+eV_hy>=-Ek(A(EP8wh@6>5>i{Jah=UrfpOJpyyEE@4x%__we9`O^8Gz zY+YB^0D>Su5CjlH;N;0u?*~Bu&N&bS0ppzGa8EB4rY4DVPDBKSF-HCU16P-HEJ7j{ zNB_V8#>OVLvurL0*LBHtT@Xo}Jb4<4WQ?)(U)V^qv$OC6A8)<&J1DB6+N!HAqgS40M1-^F1`!DUX8C&gnRGe@ zugOKk3q#X23}v$zA1@%4ZpGc13}Zxu2iAXy3X>BMLSomhm+{n7Pk?idful#^*Ezyr z?^5Ynsk5uAtC7-^+n#tfol2v%r3J6O`Z`vx?m{NB3ZCb|_3ZsV_dC<+bR%^l!h!ug z@VU>%#>bmZoH+5fD_6!g$w1(jJD&Yfx-GS5)rys1j5Ug#pWXb-IOmw2o?hS6(|2Sf zpMOr0oWEy8bh}FcG{zVdMbZCv^7OGpGT!ZY?yDjYR4SFe34j4u5I6t=RaNovCkHF@ zoiaipD2jq)GP(ESrQv7p6jU1RJ^w~D8qrNF1lzV@nHGW|z_EWH$MA4|HvlM%F)+q3 zSF1lXI(qr%k!S>tSt5A%%qNy7fY#@OJ=EhAw1( z_N}dtZ{57*k%%G? zyxTA|2q7B+k`g<9wZry251!{C8jT8 z-p;x=cW;j;6M^e_Sar{;s;=ulO|_=rc^-)9w)un*3;rOYd80A9^_8lsC>CeFcKY<0 z4{lZ}Ry-a%wttgbW>)Wx=%_7={5wQBW?I>Bfy4Z=_PG=NJe~Q9lP@w~J_0!8!Mi z9zFhP-S^kT<8fHF3CFQv7zRw!gk#&#H626*-}f;&HHG}>D5{mY;+HpXy5F+Q%Xe-M z0U*X0G)+UPbnTIkKRHlVHEm6MdpjbL2t3b)X_;^w2d-^HS2Yk3s?{on^CK7@$wLGZ zp64ZohO$2d01W`9rl;?qQ0VgL*oRY7S08R~Z--+$Fw77_rVcY?z%ng_LLsP%0zrgw zxr|)yA}WL9CMO3t=b?@zi(yy>Of!T~C+VOB*94LhM2&|}VG20s7@sU4HnuD?>4oOphsFb>PI zZUf%`%U?g-yLaC=7-Oi_YVf%apL0|y71Zl>Y~1)DcJ6!;YuDTZB0_HPA}(DTUI=X2 zCW^)4PlABI9SVie+1UxpvTpCezI`9Hzx&R+{}~^jXqkryAs~c+loA9&&G%88o5S~i zunk+bJl6RAN16Z-N~LQLgR%WmN-SO4fpAk3q?Bk*BpZN(hk5|U(0jP|^`65=ej%iQ zs;ZDuEGZ;^06r_>FlqtwW!jai!%a;Br5)J=X qQ5iHQ#>b From a4b5ded803ca42ba88de9de52ed084852fc8f9dc Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Sat, 2 Nov 2013 00:02:46 -0400 Subject: [PATCH 18/22] we no longer need to add chans to our address book --- src/bitmessagemain.py | 21 +-------------------- src/bitmessageqt/__init__.py | 5 ++--- 2 files changed, 3 insertions(+), 23 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index 3900b99d..c05b724f 100755 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -415,14 +415,6 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): if len(queueReturn) == 0: raise APIError(24, 'Chan address is already present.') address = queueReturn[0] - - #add address to addressbook - queryreturn = sqlQuery("SELECT address FROM addressbook WHERE address=?", address) - if queryreturn == []: - sqlExecute("INSERT INTO addressbook VALUES(?,?)", label, address) - shared.UISignalQueue.put(('rerenderInboxFromLabels','')) - shared.UISignalQueue.put(('rerenderSentToLabels','')) - shared.UISignalQueue.put(('rerenderAddressBook','')) return address elif method == 'joinChan': if len(params) < 2: @@ -450,14 +442,7 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): raise APIError(18, 'Chan name does not match address.') if len(addressGeneratorReturnValue) == 0: raise APIError(24, 'Chan address is already present.') - createdAddress = addressGeneratorReturnValue[0] - #add address to addressbook - queryreturn = sqlQuery("SELECT address FROM addressbook WHERE address=?", createdAddress) - if queryreturn == []: - sqlExecute("INSERT INTO addressbook VALUES(?,?)", label, createdAddress) - shared.UISignalQueue.put(('rerenderInboxFromLabels','')) - shared.UISignalQueue.put(('rerenderSentToLabels','')) - shared.UISignalQueue.put(('rerenderAddressBook','')) + createdAddress = addressGeneratorReturnValue[0] # in case we ever want it for anything. return "success" elif method == 'leaveChan': if len(params) == 0: @@ -473,10 +458,6 @@ class MySimpleXMLRPCRequestHandler(SimpleXMLRPCRequestHandler): shared.config.remove_section(address) with open(shared.appdata + 'keys.dat', 'wb') as configfile: shared.config.write(configfile) - sqlExecute('''DELETE FROM addressbook WHERE address=?''', address) - shared.UISignalQueue.put(('rerenderInboxFromLabels','')) - shared.UISignalQueue.put(('rerenderSentToLabels','')) - shared.UISignalQueue.put(('rerenderAddressBook','')) return 'success' elif method == 'deleteAddress': diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 910f73ff..bc2d80d5 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -1297,7 +1297,6 @@ class MyForm(QtGui.QMainWindow): "MainWindow", "Could not add chan because it appears to already be one of your identities.")) return createdAddress = addressGeneratorReturnValue[0] - self.addEntryToAddressBook(createdAddress, self.str_chan + ' ' + str(self.newChanDialogInstance.ui.lineEditChanNameCreate.text().toUtf8())) QMessageBox.about(self, _translate("MainWindow", "Success"), _translate( "MainWindow", "Successfully created chan. To let others join your chan, give them the chan name and this Bitmessage address: %1. This address also appears in 'Your Identities'.").arg(createdAddress)) self.ui.tabWidget.setCurrentIndex(3) @@ -1327,7 +1326,6 @@ class MyForm(QtGui.QMainWindow): "MainWindow", "Could not add chan because it appears to already be one of your identities.")) return createdAddress = addressGeneratorReturnValue[0] - self.addEntryToAddressBook(createdAddress, self.str_chan + ' ' + str(self.newChanDialogInstance.ui.lineEditChanNameJoin.text())) QMessageBox.about(self, _translate("MainWindow", "Success"), _translate( "MainWindow", "Successfully joined chan. ")) self.ui.tabWidget.setCurrentIndex(3) @@ -1637,7 +1635,8 @@ class MyForm(QtGui.QMainWindow): # Message might be to an address we own like a chan address. Let's look for that label. if shared.config.has_section(addressToLookup): toLabel = shared.config.get(addressToLookup, 'label') - toLabel = addressToLookup + if toLabel == '': + toLabel = addressToLookup self.ui.tableWidgetSent.item( i, 0).setText(unicode(toLabel, 'utf-8')) From f4fd5fd5bd7b5aa7c22e8412bec99ddb65ea50a1 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Sat, 2 Nov 2013 00:19:54 -0400 Subject: [PATCH 19/22] Improve verbage in UI --- src/bitmessageqt/__init__.py | 12 ++++++++++-- src/class_sqlThread.py | 1 + 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index bc2d80d5..6331bbb6 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -3136,13 +3136,21 @@ class settingsDialog(QtGui.QDialog): self.ui.checkBoxPortableMode.setChecked(True) if 'darwin' in sys.platform: self.ui.checkBoxStartOnLogon.setDisabled(True) + self.ui.checkBoxStartOnLogon.setText(_translate( + "MainWindow", "Start-on-login not yet supported on your OS.")) self.ui.checkBoxMinimizeToTray.setDisabled(True) + self.ui.checkBoxMinimizeToTray.setText(_translate( + "MainWindow", "Minimize-to-tray not yet supported on your OS.")) self.ui.checkBoxShowTrayNotifications.setDisabled(True) - self.ui.labelSettingsNote.setText(_translate( - "MainWindow", "Options have been disabled because they either aren\'t applicable or because they haven\'t yet been implemented for your operating system.")) + self.ui.checkBoxShowTrayNotifications.setText(_translate( + "MainWindow", "Tray notifications not yet supported on your OS.")) elif 'linux' in sys.platform: self.ui.checkBoxStartOnLogon.setDisabled(True) + self.ui.checkBoxStartOnLogon.setText(_translate( + "MainWindow", "Start-on-login not yet supported on your OS.")) self.ui.checkBoxMinimizeToTray.setDisabled(True) + self.ui.checkBoxMinimizeToTray.setText(_translate( + "MainWindow", "Minimize-to-tray not yet supported on your OS.")) # On the Network settings tab: self.ui.lineEditTCPPort.setText(str( shared.config.get('bitmessagesettings', 'port'))) diff --git a/src/class_sqlThread.py b/src/class_sqlThread.py index 24679acb..7e1349d4 100644 --- a/src/class_sqlThread.py +++ b/src/class_sqlThread.py @@ -275,6 +275,7 @@ class sqlThread(threading.Thread): shared.config.set('bitmessagesettings', 'useidenticons', 'True') if not shared.config.has_option('bitmessagesettings', 'identiconsuffix'): # acts as a salt shared.config.set('bitmessagesettings', 'identiconsuffix', ''.join(random.choice("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz") for x in range(12))) # a twelve character pseudo-password to salt the identicons + # Since we've added a new config entry, let's write keys.dat to disk. with open(shared.appdata + 'keys.dat', 'wb') as configfile: shared.config.write(configfile) From 162114ab28d84c9dc75a38da55aea976e9f9b1df Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Sat, 2 Nov 2013 17:34:46 -0400 Subject: [PATCH 20/22] load Sent To label from subscriptions if available --- src/bitmessageqt/__init__.py | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index bc2d80d5..3412b828 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -706,6 +706,15 @@ class MyForm(QtGui.QMainWindow): if queryreturn != []: for row in queryreturn: toLabel, = row + if toLabel == '': + # It might be a broadcast message. We should check for that + # label. + queryreturn = sqlQuery( + '''select label from subscriptions where address=?''', toAddress) + + if queryreturn != []: + for row in queryreturn: + toLabel, = row if toLabel == '': if shared.config.has_section(toAddress): @@ -831,7 +840,7 @@ class MyForm(QtGui.QMainWindow): if shared.config.has_section(fromAddress): fromLabel = shared.config.get(fromAddress, 'label') - if fromLabel == '': # If the fromAddress isn't one of our addresses + if fromLabel == '': # If the fromAddress isn't one of our addresses and isn't a chan queryreturn = sqlQuery( '''select label from addressbook where address=?''', fromAddress) if queryreturn != []: @@ -839,7 +848,7 @@ class MyForm(QtGui.QMainWindow): fromLabel, = row if fromLabel == '': # If this address wasn't in our address book... - queryReturn = sqlQuery( + queryreturn = sqlQuery( '''select label from subscriptions where address=?''', fromAddress) if queryreturn != []: for row in queryreturn: From 3b41eafa7b3d6e71322e2f3c9176cd166827d188 Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Sat, 2 Nov 2013 18:01:36 -0400 Subject: [PATCH 21/22] revert main window to previous size --- src/bitmessageqt/bitmessageui.py | 22 ++++++---------------- src/bitmessageqt/bitmessageui.ui | 10 +++++----- 2 files changed, 11 insertions(+), 21 deletions(-) diff --git a/src/bitmessageqt/bitmessageui.py b/src/bitmessageqt/bitmessageui.py index d8ac9484..b80367dd 100644 --- a/src/bitmessageqt/bitmessageui.py +++ b/src/bitmessageqt/bitmessageui.py @@ -2,8 +2,8 @@ # Form implementation generated from reading ui file 'bitmessageui.ui' # -# Created: Sat Sep 21 15:59:39 2013 -# by: PyQt4 UI code generator 4.10.2 +# Created: Sat Nov 2 18:01:09 2013 +# by: PyQt4 UI code generator 4.10 # # WARNING! All changes made in this file will be lost! @@ -26,7 +26,7 @@ except AttributeError: class Ui_MainWindow(object): def setupUi(self, MainWindow): MainWindow.setObjectName(_fromUtf8("MainWindow")) - MainWindow.resize(795, 580) + MainWindow.resize(885, 580) icon = QtGui.QIcon() icon.addPixmap(QtGui.QPixmap(_fromUtf8(":/newPrefix/images/can-icon-24px.png")), QtGui.QIcon.Normal, QtGui.QIcon.Off) MainWindow.setWindowIcon(icon) @@ -439,7 +439,7 @@ class Ui_MainWindow(object): self.gridLayout.addWidget(self.tabWidget, 0, 0, 1, 1) MainWindow.setCentralWidget(self.centralwidget) self.menubar = QtGui.QMenuBar(MainWindow) - self.menubar.setGeometry(QtCore.QRect(0, 0, 795, 18)) + self.menubar.setGeometry(QtCore.QRect(0, 0, 885, 27)) self.menubar.setObjectName(_fromUtf8("menubar")) self.menuFile = QtGui.QMenu(self.menubar) self.menuFile.setObjectName(_fromUtf8("menuFile")) @@ -557,8 +557,8 @@ class Ui_MainWindow(object): self.textEditMessage.setHtml(_translate("MainWindow", "\n" "\n" -"


", None)) +"\n" +"


", None)) self.label.setText(_translate("MainWindow", "To:", None)) self.label_2.setText(_translate("MainWindow", "From:", None)) self.radioButtonBroadcast.setText(_translate("MainWindow", "Broadcast to everyone who is subscribed to your address", None)) @@ -642,13 +642,3 @@ class Ui_MainWindow(object): self.actionJoinChan.setText(_translate("MainWindow", "Join / Create chan", None)) import bitmessage_icons_rc - -if __name__ == "__main__": - import sys - app = QtGui.QApplication(sys.argv) - MainWindow = QtGui.QMainWindow() - ui = Ui_MainWindow() - ui.setupUi(MainWindow) - MainWindow.show() - sys.exit(app.exec_()) - diff --git a/src/bitmessageqt/bitmessageui.ui b/src/bitmessageqt/bitmessageui.ui index a802cdac..e5148ec1 100644 --- a/src/bitmessageqt/bitmessageui.ui +++ b/src/bitmessageqt/bitmessageui.ui @@ -6,7 +6,7 @@ 0 0 - 795 + 885 580 @@ -278,8 +278,8 @@ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:9pt; font-weight:400; font-style:normal;"> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p></body></html> +</style></head><body style=" font-family:'Sans'; font-size:9pt; font-weight:400; font-style:normal;"> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2';"><br /></p></body></html>
@@ -1054,8 +1054,8 @@ p, li { white-space: pre-wrap; } 0 0 - 795 - 18 + 885 + 27 From e5fce78fc38d951707a5e3dbf6885d94b7387eae Mon Sep 17 00:00:00 2001 From: Jonathan Warren Date: Sat, 2 Nov 2013 18:05:39 -0400 Subject: [PATCH 22/22] revert settings window as close as possible to previous size --- src/bitmessageqt/settings.py | 4 ++-- src/bitmessageqt/settings.ui | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/bitmessageqt/settings.py b/src/bitmessageqt/settings.py index 152ab0b7..b246b5ce 100644 --- a/src/bitmessageqt/settings.py +++ b/src/bitmessageqt/settings.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'settings.ui' # -# Created: Fri Nov 1 19:14:45 2013 +# Created: Sat Nov 2 18:03:44 2013 # by: PyQt4 UI code generator 4.10 # # WARNING! All changes made in this file will be lost! @@ -26,7 +26,7 @@ except AttributeError: class Ui_settingsDialog(object): def setupUi(self, settingsDialog): settingsDialog.setObjectName(_fromUtf8("settingsDialog")) - settingsDialog.resize(646, 473) + settingsDialog.resize(585, 437) self.gridLayout = QtGui.QGridLayout(settingsDialog) self.gridLayout.setObjectName(_fromUtf8("gridLayout")) self.buttonBox = QtGui.QDialogButtonBox(settingsDialog) diff --git a/src/bitmessageqt/settings.ui b/src/bitmessageqt/settings.ui index b7d7ae22..78a67aa5 100644 --- a/src/bitmessageqt/settings.ui +++ b/src/bitmessageqt/settings.ui @@ -6,8 +6,8 @@ 0 0 - 646 - 473 + 585 + 437