From af71cdc6260fc737a47d639a31cdac8d1abc89ba Mon Sep 17 00:00:00 2001 From: cncfanatics Date: Thu, 19 Aug 2010 15:43:51 +0200 Subject: [PATCH] Start work on the ship browser, the ship browser part is ready. Missing a fit listing and buttons to create, rename, duplicate, etc. a fit --- controller/market.py | 21 ++++++++- gui/fittingView.py | 8 ++++ gui/mainFrame.py | 32 ++++++++++--- gui/mainToolBar.py | 23 ++++++++-- gui/marketBrowser.py | 2 +- gui/shipBrowser.py | 80 +++++++++++++++++++++++++++++++++ icons/race_amarr_small.png | Bin 0 -> 684 bytes icons/race_angel_small.png | Bin 0 -> 774 bytes icons/race_blood_small.png | Bin 0 -> 630 bytes icons/race_caldari_small.png | Bin 0 -> 877 bytes icons/race_gallente_small.png | Bin 0 -> 832 bytes icons/race_guristas_small.png | Bin 0 -> 764 bytes icons/race_minmatar_small.png | Bin 0 -> 853 bytes icons/race_ore_small.png | Bin 0 -> 797 bytes icons/race_sansha_small.png | Bin 0 -> 806 bytes icons/race_serpentis_small.png | Bin 0 -> 817 bytes 16 files changed, 153 insertions(+), 13 deletions(-) create mode 100644 gui/shipBrowser.py create mode 100644 icons/race_amarr_small.png create mode 100644 icons/race_angel_small.png create mode 100644 icons/race_blood_small.png create mode 100644 icons/race_caldari_small.png create mode 100644 icons/race_gallente_small.png create mode 100644 icons/race_guristas_small.png create mode 100644 icons/race_minmatar_small.png create mode 100644 icons/race_ore_small.png create mode 100644 icons/race_sansha_small.png create mode 100644 icons/race_serpentis_small.png diff --git a/controller/market.py b/controller/market.py index c7f07a4fd..1b7bda38e 100644 --- a/controller/market.py +++ b/controller/market.py @@ -38,7 +38,7 @@ class Market(): items = [] group = eos.db.getMarketGroup(id) - for item in group.items: + for item in group.items: icon = item.icon.iconFile if item.icon else "" items.append((item.ID, item.name, icon)) @@ -59,6 +59,23 @@ class Market(): return children + def getShipRoot(self): + cat = eos.db.getCategory(6) + root = [] + for grp in cat.groups: + if grp.published == 1: + root.append((grp.ID, grp.name)) + + return root + + def getShipList(self, id): + ships = [] + grp = eos.db.getGroup(id) + for item in grp.items: + if item.published == 1: + ships.append((item.ID, item.name, item.race)) + + return ships def getMarketRoot(self): """ Get the root of the market tree. @@ -75,6 +92,6 @@ class Market(): root = [] for id in marketGroups: mg = eos.db.getMarketGroup(id) - root.append((id, mg.name, mg.icon.iconFile)) + root.append((id, mg.name, mg.icon.iconFile if mg.icon else "")) return root diff --git a/gui/fittingView.py b/gui/fittingView.py index 3ea287862..71d199b04 100644 --- a/gui/fittingView.py +++ b/gui/fittingView.py @@ -32,6 +32,8 @@ class FittingView(wx.ListCtrl): self.SetImageList(self.imageList, wx.IMAGE_LIST_SMALL) self.activeColumns = [] self.Bind(wx.EVT_LIST_COL_BEGIN_DRAG, self.resizeChecker) + self.Bind(wx.EVT_LIST_COL_CLICK, self.dragCheck) + self.Bind(wx.EVT_LIST_COL_END_DRAG, self.dragCheck) i = 0 for colName in FittingView.DEFAULT_COLS: col = gui.builtinViewColumns.getColumn(colName)(self, None) @@ -48,3 +50,9 @@ class FittingView(wx.ListCtrl): def resizeChecker(self, event): if self.activeColumns[event.Column].resizable is False: event.Veto() + + def dragCheck(self, event): + print event + + def dragEnd(self, event): + print event diff --git a/gui/mainFrame.py b/gui/mainFrame.py index 15affba33..2645ac23f 100644 --- a/gui/mainFrame.py +++ b/gui/mainFrame.py @@ -23,25 +23,39 @@ from gui.mainToolBar import MainToolBar from gui.marketBrowser import MarketBrowser from gui.fitMultiSwitch import FitMultiSwitch from gui.statsPane import StatsPane +from gui.shipBrowser import ShipBrowser from wx.lib.wordwrap import wordwrap import aboutData class MainFrame(wx.Frame): + __instance = None + @classmethod + def getInstance(cls): + return cls.__instance if cls.__instance is not None else MainFrame() + def __init__(self): wx.Frame.__init__(self, None, wx.ID_ANY, title="pyfa - Python Fitting Assistant") + MainFrame.__instance = self + self.SetMinSize((1000, 700)) - #Add menu - self.SetMenuBar(MainMenuBar()) - self.SetToolBar(MainToolBar(self)) - - #Register menubar events / only quit for now + #Register menubar events / only quit for now self.Bind(wx.EVT_MENU, self.ExitApp, id=wx.ID_EXIT) self.Bind(wx.EVT_MENU, self.ShowAboutBox, id=wx.ID_ABOUT) self.splitter = wx.SplitterWindow(self, style = wx.SP_LIVE_UPDATE) - self.marketBrowser = MarketBrowser(self.splitter) + marketShipBrowserPanel = wx.Panel(self.splitter) + self.marketShipBrowserSizer = wx.BoxSizer(wx.VERTICAL) + marketShipBrowserPanel.SetSizer(self.marketShipBrowserSizer) + + self.marketBrowser = MarketBrowser(marketShipBrowserPanel) + self.marketShipBrowserSizer.Add(self.marketBrowser, 1, wx.EXPAND) + + + self.shipBrowser = ShipBrowser(marketShipBrowserPanel) + self.marketShipBrowserSizer.Add(self.shipBrowser, 1, wx.EXPAND) + statsFitviewPanel = wx.Panel(self.splitter) sizer = wx.BoxSizer(wx.HORIZONTAL) statsFitviewPanel.SetSizer(sizer) @@ -54,10 +68,14 @@ class MainFrame(wx.Frame): sizer.Add(self.fitMultiSwitch, 1, wx.EXPAND) sizer.Add(self.statsPane, 0, wx.EXPAND) - self.splitter.SplitVertically(self.marketBrowser, statsFitviewPanel) + self.splitter.SplitVertically(marketShipBrowserPanel, statsFitviewPanel) self.splitter.SetMinimumPaneSize(10) self.splitter.SetSashPosition(300) + #Add menu + self.SetMenuBar(MainMenuBar()) + self.SetToolBar(MainToolBar(self)) + #Show ourselves self.Show() diff --git a/gui/mainToolBar.py b/gui/mainToolBar.py index 9182a91e7..9921961f4 100644 --- a/gui/mainToolBar.py +++ b/gui/mainToolBar.py @@ -19,12 +19,29 @@ import wx from gui import bitmapLoader +import gui.mainFrame class MainToolBar(wx.ToolBar): def __init__(self, parent): - wx.ToolBar.__init__(self, parent, wx.ID_ANY) + style = wx.TB_HORIZONTAL | wx.NO_BORDER | wx.TB_FLAT + wx.ToolBar.__init__(self, parent, wx.ID_ANY, style=style) - self.AddCheckLabelTool(wx.ID_ANY, "Ship Browser", bitmapLoader.getBitmap("ship_big", "icons")) - self.AddLabelTool(wx.ID_ANY, "Character Editor", bitmapLoader.getBitmap("character_big", "icons")) + self.AddCheckLabelTool(10, "Ship Browser", bitmapLoader.getBitmap("ship_big", "icons"), shortHelp="Ship browser") + self.AddCheckLabelTool(20, "Character Editor", bitmapLoader.getBitmap("character_big", "icons"), shortHelp="Character editor") + self.Bind(wx.EVT_TOOL, self.shipBrowserToggle, id=10) + self.Bind(wx.EVT_TOOL, self.characterEditor, id=20) self.Realize() + + gui.mainFrame.MainFrame.getInstance().shipBrowser.Hide() + + def shipBrowserToggle(self, event): + newState = self.GetToolState(10) + mainFrame = gui.mainFrame.MainFrame.getInstance() + + mainFrame.shipBrowser.Show(newState) + mainFrame.marketBrowser.Show(not newState) + mainFrame.marketShipBrowserSizer.Layout() + + def characterEditor(self, event): + print event diff --git a/gui/marketBrowser.py b/gui/marketBrowser.py index 1e66d9014..c4e3f816c 100644 --- a/gui/marketBrowser.py +++ b/gui/marketBrowser.py @@ -28,7 +28,7 @@ class MarketBrowser(wx.Panel): vbox = wx.BoxSizer(wx.VERTICAL) self.splitter = wx.SplitterWindow(self, style = wx.SP_LIVE_UPDATE) - + vbox.Add(self.splitter, 1, wx.EXPAND) self.SetSizer(vbox) diff --git a/gui/shipBrowser.py b/gui/shipBrowser.py new file mode 100644 index 000000000..46d8a1877 --- /dev/null +++ b/gui/shipBrowser.py @@ -0,0 +1,80 @@ +import wx +import controller +import bitmapLoader + +class ShipBrowser(wx.Panel): + def __init__(self, parent): + wx.Panel.__init__(self, parent) + vbox = wx.BoxSizer(wx.VERTICAL) + + self.splitter = wx.SplitterWindow(self, style = wx.SP_LIVE_UPDATE) + + vbox.Add(self.splitter, 1, wx.EXPAND) + self.SetSizer(vbox) + + self.shipView = ShipView(self.splitter) + + listStyle = wx.LC_REPORT | wx.BORDER_NONE | wx.LC_NO_HEADER | wx.LC_SINGLE_SEL + self.fitView = wx.ListCtrl(self.splitter, style = listStyle) + + self.shipImageList = wx.ImageList(16, 16) + self.shipView.SetImageList(self.shipImageList) + + self.splitter.SplitHorizontally(self.shipView, self.fitView) + self.splitter.SetMinimumPaneSize(250) + + self.shipRoot = self.shipView.AddRoot("Ships") + + iconId = self.shipImageList.Add(bitmapLoader.getBitmap("ship_small", "icons")) + + cMarket = controller.Market.getInstance() + shipRoot = cMarket.getShipRoot() + for id, name in shipRoot: + childId = self.shipView.AppendItem(self.shipRoot, name, iconId, data=wx.TreeItemData(id)) + self.shipView.AppendItem(childId, "dummy") + + self.shipView.SortChildren(self.shipRoot) + + self.raceImageIds = {} + self.races = ["amarr", "caldari", "gallente", "minmatar", "ore", "serpentis", "angel", "blood", "sansha", "guristas"] + for race in self.races: + imageId = self.shipImageList.Add(bitmapLoader.getBitmap("race_%s_small" % race, "icons")) + self.raceImageIds[race] = imageId + + #Bind our lookup method to when the tree gets expanded + self.shipView.Bind(wx.EVT_TREE_ITEM_EXPANDING, self.expandLookup) + self.idRaceMap = {} + self.shipView.races = self.races + self.shipView.idRaceMap = self.idRaceMap + + def expandLookup(self, event): + root = event.Item + child, cookie = self.shipView.GetFirstChild(root) + self.idRaceMap.clear() + if self.shipView.GetItemText(child) == "dummy": + self.shipView.Delete(child) + + cMarket = controller.Market.getInstance() + + for id, name, race in cMarket.getShipList(self.shipView.GetPyData(root)): + iconId = self.raceImageIds[race] if race in self.raceImageIds else -1 + self.idRaceMap[id] = race + self.shipView.AppendItem(root, name, iconId, data=wx.TreeItemData(id)) + + self.shipView.SortChildren(root) + +class ShipView(wx.TreeCtrl): + def __init__(self, parent): + wx.TreeCtrl.__init__(self, parent) + treeStyle = self.GetWindowStyleFlag() + treeStyle |= wx.TR_HIDE_ROOT + self.SetWindowStyleFlag(treeStyle) + + def OnCompareItems(self, treeId1, treeId2): + child, cookie = self.GetFirstChild(treeId1) + if child.IsOk(): + return cmp(self.GetItemText(treeId1), self.GetItemText(treeId2)) + else: + id1 = self.GetPyData(treeId1) + id2 = self.GetPyData(treeId2) + return cmp(self.races.index(self.idRaceMap[id1]), self.races.index(self.idRaceMap[id2])) diff --git a/icons/race_amarr_small.png b/icons/race_amarr_small.png new file mode 100644 index 0000000000000000000000000000000000000000..2e9f72cff3498303be2d5ecd66abd7cf789b8ae9 GIT binary patch literal 684 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4(FKU@G=>aSW-Lla#OkB*pOm{~`%S z6xzvgjgHPT9v+@GKdx-pxKYa3z@Xs34~W{NCc}ssGjm#4cf6gTx-sg|8I^^j&YNkl+<(E-=>1lLjM2%{{<-Z5{SP7F({CvgZ(TEw3JvJHy?R?jf0(C^~cYjr-9=C|AUPJIf+4C zUU-g&jhfZ0HK*rI%+~KzvJZUTIeGtr%U3R+11kKZp(3T@Z6zDe#r#{fcKYF!a-y7a zItIE7OV%6+Ko|uwiNW7q{VoR^%OoIEo|T0;q^8L2>gTsp{=GOH`+swn{r{!}y?>6z za!=S;nP&pkSTHg&TBQY>e+IIxfcXFaqy!)^xIFRp=>rD@P8>LJfamLM6=mhc@e>wb zdYKV^)xC#@ClnZSazK2^!1UR!P3Dpk5;BmW1#3=7Pv%WXOX70s3u9wrb5nD36Xq+s zCIn=2g7`aFlo#`ut4FgkGdDAVt!z?f21=;1opCvG1ZZJzngKHq*aPKNfHXH)jgwf4 l%eGdAhSjWKE_#AuX4v|+=7!iD)nuSFgQu&X%Q~loCIDC}044wc literal 0 HcmV?d00001 diff --git a/icons/race_angel_small.png b/icons/race_angel_small.png new file mode 100644 index 0000000000000000000000000000000000000000..02facd522ed52ad7d846c8dc3fc78b4099bb3fc2 GIT binary patch literal 774 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4(FKU^?sR;uumfCn;e8NQ&YA|3h67 z5fL{ffMDX$A3uMxN=i!3yl|BXA}0_K8oK%Evxl6C2?-Be#QuMKD;@9P@E{~DEiEpk z5MqFndBfcU7ixYT=YJn>U|{gaZQ75|&(ALoUhZdaZepU9mXeav+{p|!&&kliA;6;1 zzu~($GxK5V66SyZf#CW2`Qd+=l^+8+`n3xPce8nb;McEX z$7=pKZSwK)d1YV-wcYT+-`(c(@9p_{X8wc;0f!D8IB*t-j~qI5=oFBC00cRCZ-ah4 z__wznWLHW9n-SOs21kZ{{~1As$jQl>zIyfQ@6VqP z+uBnnPOLb5;DExZBS(&a!V0R@pMAf?j2SBZvX&nnKRqq}ddie5=XK+DoqF*8zWv|p z>*7xR{rGvmgt75vD`33MuK)k@1xbpRD>G`P%2?c2mUeBQF+h$Z$R50z_xpOX1!wn!_0>nQoEG+*5=@=kxnIH;D xFNa!ZNJ>alEHtsH0Sa4O5QQYmMG}x8V`f;RwO82De7!cvWKUN=mvv4FO#m+!YYzYb literal 0 HcmV?d00001 diff --git a/icons/race_blood_small.png b/icons/race_blood_small.png new file mode 100644 index 0000000000000000000000000000000000000000..0e7577a82e3cd3c17ceca369c26b68972b416c53 GIT binary patch literal 630 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4(FKV6ydeaSW-Lla#OkB*pOm{~`%S zFrDNgzTwJM-p`ePt2mAI^_lgpt(%S1)ek%S`L*dMCDnIdUvn`%Az?@B8aar1NAm~Q z|Nrkf9=|8yY2C-BhwRME&5YdK%}4+L=V5>PWQL@Kgvmu-h$V*%9@sN8AFfYJYg0eu z#N(Ei$5SKohUa|#y|jdy4-Bitp~|ujY~Nom$)b8xLM!^V#Ekm?2`@bSQUwx{4rRd% zYG`b11lh%GzF*+*tEJu&argEf{_yNGdz*g5R$~L0K|13Z|Cr*5L|G$0>C|H3Zc*vlkwXyMFxbM`5^Eb&fzj|%X6J=$^qpv?F zsp8WwWw?bL%w}wCZRI&RgUjn{vE&u>4yzs~S~ zdP+*gr~m)2OY`uk17pB$!D@D>;;9oSPW<@)|NkZ*9v(9*9-csb35gx?h6Znv5)y74 zKX9O+9~iLKo2r1yL59E5QL&U`^Vm@1e4PUtWXue^c65B@+$%Q^q{h?L&t;ucLK6Uh CAp^Al literal 0 HcmV?d00001 diff --git a/icons/race_caldari_small.png b/icons/race_caldari_small.png new file mode 100644 index 0000000000000000000000000000000000000000..f21c987927f79cf2d6fb47ca0561b61c53cc679e GIT binary patch literal 877 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4(FKU^ek|aSW-Lla#OkB*pOm{~`%S zFr9QnAR*;RaA2r@!RIf$Vb`w&J$d%%Pu<^BJ1-Zfot^dd;oYWT5 zrcS+Tp`el^EUdg)L|ORq{rdm^>#tn7l;-X2{ZR1P^F$A?j_9p9H#eEEv9Q}=Eg>PXBk>nR zW0SIG=FQA9zjgmvTU+=xZ`yd2wbk|Aoze$4t|uSww+{{v*G@`FG5Pp`w`S6eX@95B zoOyG4V`F0{5JP;}WHs0K{-Im%rf*yKFC6G36KU(?dq1wc`CdhJ={4(}JKpXtdVTT# zhZ8UMKD_wx{n{Lvf7)8B*Q6yRJV;GQNPswk!I5F#e-;p1NLa+0fq{|r`?s(6fZX#( z4(;Ct#3n%YmFLf&z0b)=+04Po>Gbu>7lvQIelhUy@M!_%M1c4+5dZ&ga?X*fK|#Rb z;`+xtj;j~_mOs-PKEcVu;8@r`;XPM9(vB6zF>LSf2|eq;nDzesyLUbL0!d4@sxwF* z;0I|3yMzg762qrYAFlE6@c#Dj@mFMIWR&ss514Z6_MKBczP^(Kg2Q$S3JdF;J9Fa1 z=T9Hru&}T&ynp-lK2X~ikX|78|G(*I!-B3|$3CfMZ~NML7*E@szPd-e4n z6vV}+s|yQH*3{NsJ-cN|$lSe)UsqSFZ~q^2=gw?N2??IWOb)Q!PM#jXn6LVHQu1`< z&7E6z?6i=q`~UCj@893^&-BaLB>ejP{QCXP%lqE{{PVNeo|*Y@9WdtNfr*9(YO(*R z9w29ujEqcX(4|dsOiWHj_xIQT{rvCm?soln8~YFc|NjRiTybDpsyhUV9-xm8o!|~Q zaO%JTg(Ec5IG`jBI*&@BaV)_c!^1>9zELkg${=zy4|;?u|Cz zKHskPmk2ZSVQV%ZfLNa7R4&AA%zb$J`T3G{e}A0)@Z_SfTNz*3vGDV81{(}E8+_RG z+2GHgzmuCA8yh8;$$>2mIk%CQL4kwifO$gW@B9wg-k&bkM`v-qJM~~6_auSP2`#+N zX->P$=J#zjQ(Dv@5AvjwVZ+RsGaG02e*ORMtM#`3$q7GJEMKi%_UXU=y8ZY6*X^kO z{_gUsb+ewlzrSy>1jKDlADNVum6`eZ&6TC4f9G1V-Ykz{y5l5&zkbh2^%Lhme13ku zIo_`R=lKu+?YkJEdI}Hj-~V4?M`gvoqJj;dW38%JKD%_&n~R5s=llBjU3&UzdRbrI zoxQ!k{@*`WL#Q6ZhKZdM9RovO+V7vhu>8k%`Deh8+V%PUeS5nwvoN)|I=hSE`f>lA zJImqW=G4vV)~3!cXXrE2?5eG*?aq0_XS3_C+1J<4tpE3;a}hH* z%$$rL*f;N(!os~;VfKT^&;9q^w!HP5o0FUM-pdG|p`8e0=|Zy$lfWEJ{k^k-xX^|F369UsnS|i_>+ z`SH`oWm(dh?x(f2J8FM^n)>kX>+kQ^&)Z$|<8gbxw1k92pP_-lokmF}h(`VpiG=io zgbB&%>HWzmDHDEweEi&xjje4m(DZ&u35hyIZHN_0EfGL2%L0=yphjSNU}ktQ=d}#S R!G*0LzNf37%Q~loCIHkLbtM1* literal 0 HcmV?d00001 diff --git a/icons/race_guristas_small.png b/icons/race_guristas_small.png new file mode 100644 index 0000000000000000000000000000000000000000..2eb858f76530e1b877bc8cdbb2f3e01d9327d68d GIT binary patch literal 764 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4(FKU^?vS;uumfCn@0o^AaHF;o}>i+*fq_twX+Iy*oNpau4|4lwDTz>yEs+nVIKLoE3BHvblWz#}5Vj_y2wo$;!-p*gT~bs1#^!QlsIF z88a-VENQXvEisuYZ&UyG+mofUv;PCZ*H3fZ|LbfO)ta~Wf7R5+#zyJ8AWa}$i3te_ z1{W@t7z+M0etY%#eEWCz_Wr#S5Ev15BEmEM_RQU;|Nj}+B>eg8{NVNW_xCT(J%8Z9 z0VKUU3ZMU*$sJyo;<)$7?{9~WV@a#I4Wt4q&OUXKIFRuDsl}{%bu5Y?FwbeUpDag^fn?L~u3b9s+pO%r@ z%{(su+a){x+>d+nq`JbSUvay>q7CZ2S8EMlZBGj0`@0 z{`2$v@7V_fA9SjVThG=92BNW#B1G}QWwTpndiun)oDZ1LJMrPO+4J?6`B`sDUCWSO z{Vzc28W`Bj3>I2vq+(U{E`a!+u6{1-oD!M<>MdMB literal 0 HcmV?d00001 diff --git a/icons/race_minmatar_small.png b/icons/race_minmatar_small.png new file mode 100644 index 0000000000000000000000000000000000000000..b551d94372262100785fe19f7a7351e3906a559e GIT binary patch literal 853 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4(FKV3zT8aSW-Lla#OkB*pOm{~`%S zFr9QnAR*;RdScqNm7Toti}h4Bj~_bs^S`b5`Mb+HC#&1k{Yl|uWl$>~`^ z!G_nmdAU1I96g#@`tz3V%ca)e3)kM?Is5nh-t4OX|Naj|Lu2PmU1Qt3_YWP4 zsrtF;>GJG9dl#9#vp@0SioGwi+;R5KmWab-Cw(t2M!dpG&Vkz@-b!x0-&z{{|}wu4#+n&HZb@D tN{7uy&4Izf4>WuJh6Bx@;6=&-%nVU)#KK~He=Y=xd%F6$taD0e0s!O7uu=d3 literal 0 HcmV?d00001 diff --git a/icons/race_ore_small.png b/icons/race_ore_small.png new file mode 100644 index 0000000000000000000000000000000000000000..4e560970d8746ff8c86f7d31dff93baf23756899 GIT binary patch literal 797 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4(FKV0z-|;uumfCn;e8NQ&YA|3wmv zD0GsG_=D%qg(pn}fk`0`9zJ}S2n4@={{H^``uh9(_Emp>cXzseyxo5wu%A%{F@?dA zVc&m{K@416oPs7=awTr&>H$oDz9~I^`iS*E6X!oZem({PAXZRRVAMA@W|ovz{`27d z-}_}%t@HNm**g)a^#>6D|BoYw?Ijg1e1uIJfb_cyKb=QHN(_a09#uDP3=Eg>OM0mKp@)Bpc(l0I<$=&=v)|JSRr zv9-PD;o;f;^Jm4>Vk2SUy1%KZGfXN^O^M%MxAz~=tmW(D_uKya4OBbB4x&4$$uJ^v zhQz-=Kfep7X>&i8e`_(9-_-Xbm+6@^K6yPoJvu-Gd)}<^`CnJ_`xrwoJH*c24O4q( zKD__`|Ncaw_ja}RX;U_(7|Fk{dMR&TSJ&A3$DXaNKHh$B`);TkB@Uc8ap35&V+}IP z|Nl4M^Y_>1_@me7&$k5nqRz;`;7@8o!Vi5FrGx)p9=`qGpd0G8#$&3Fr@y!V_gu9? za@~z$-ESW!iyybI{RfPa`S$lgdN*#~%+0~cIZH}9OIo%1rJC)o+E&3dh;~F2F)=Z* zGBNy*kQHW6wbYhS_V%zARa4huVP)fB{Q2w0|F>_RGh9A@+S$XpR#*>OXsYe6d(%h_44W=6!7#Kh_Bqb#zBovfv@;|HV@k>EVOW=^c z%83K#&Yk-3-~Ld7tn*y?qONB=^Y_&zW~}kox2sDyY7Ml{6o^5d2KtY|kzwC|77&}4 zmsk4U{k#0aqQVTU%!~|@QX&jO{6c!cAzpjG|NQ&Q#ztTB(SxH5XD{7hICbJO^UG(i zxPjWVf%p~>BO7(@+=aE`lA@{#D)LbpdTI>naxx58uH5?|y7EbKsIUc-%QQcg=tdtAHx+TWVr zZ!i6~O`rB}`vyb1GY1YlICbDaLTbXA(sQTJKiofk{@?mi-qz(jJUpC0ECa+mAVdED zPimC>@csS$&+p70D$YFm@v>;h(Q> zFpbnm^DX;iB(`XZHm6sbWwcbp($uY-k`x;b^SYP&bZ;qC4?NE~Jcs{}^FN$Bxj83= z{3t#EK$xACna9LX_VR+6SL%Uk0D`FO%#3`Z1;^Ui5`eH1-SJPvH5i7;6^a*0%9L`o zPN$}zONA)KX-IN37l}wdK=z#1OB5FihMcSG11mIyP&Ch?7#7F?X2@alU6El5A2qB0 zv9bpE{G9+1@xlJYXdp$yz`09ZO`e}Xn2@mNbRgjOL7D~}PB3qFZo$Y%H~&Kgm_u!N zf!FH?ybvx37e@e!0-&L5+`8!x8gM#0p7S?X1s~oo0Q1N+5c0X8v~uAngCT#TIKxT& zYPsOpgtVzMt1&9HaI~mh?54TpVY=Sf)O}q>R|<%$x&CCTaX2y~Ykcgouii6Q+AdqM zz7)5b!V?jBwUl*-)8l2iv(Q5 zqK5E)KSa*7snDW9LMTF*R#=9bVPGXCc<%Ilt$i8S$`yH9dD0oJeJP(AJY{o#wc6lJ znF=G;za^91BB^BIm2b@buvu8PMPR$Yii@?OO_8|VG>OXlwS&kvQn_MWx>36Veu13pumvG=gC zsK2t!F>dM+xEA#q`xmvA@OeW%vxIFMRKRC5%v~+2BIq@7e1vBTs?2)rgQVi|4uf<^ zdZl8b5Ux78w;+_D1l5O{^_y>E4<-nde=$B9SKKu*X>^MY!|aSR%4V@x#$LZ&I}%Uo z)^6T4Ttg)g`sqL#X_BeXf=>nL?(`~~;H-5EvbXM3g9JtME5_!FF|6;;E{{BJOZ`*d a-fjf=y65l3I9e0f|LhYvna_@6HNOB(6Kx9s literal 0 HcmV?d00001