From 0327ee9a2695689f93e590b8a32278eb3d0d41e6 Mon Sep 17 00:00:00 2001 From: Ellpeck Date: Sat, 12 Nov 2016 12:26:36 +0100 Subject: [PATCH] Added search bar --- .../api/booklet/IBookletEntry.java | 3 +- .../mod/booklet/entry/BookletEntry.java | 65 +++++++++++++++++- .../mod/booklet/gui/GuiBooklet.java | 64 ++++++++++++++++- .../mod/booklet/gui/GuiEntry.java | 29 +++++--- .../mod/booklet/gui/GuiMainPage.java | 2 +- .../mod/booklet/misc/BookletUtils.java | 2 +- .../gui/booklet/guiBookletGadgets.png | Bin 5467 -> 5822 bytes 7 files changed, 147 insertions(+), 18 deletions(-) diff --git a/src/main/java/de/ellpeck/actuallyadditions/api/booklet/IBookletEntry.java b/src/main/java/de/ellpeck/actuallyadditions/api/booklet/IBookletEntry.java index ae60daa37..ae345e8a2 100644 --- a/src/main/java/de/ellpeck/actuallyadditions/api/booklet/IBookletEntry.java +++ b/src/main/java/de/ellpeck/actuallyadditions/api/booklet/IBookletEntry.java @@ -10,7 +10,6 @@ package de.ellpeck.actuallyadditions.api.booklet; -import de.ellpeck.actuallyadditions.api.booklet.internal.GuiBookletBase; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; @@ -29,5 +28,5 @@ public interface IBookletEntry{ void addChapter(IBookletChapter chapter); @SideOnly(Side.CLIENT) - List getChaptersForGuiDisplaying(GuiBookletBase gui, String searchBarText); + List getChaptersForDisplay(String searchBarText); } diff --git a/src/main/java/de/ellpeck/actuallyadditions/mod/booklet/entry/BookletEntry.java b/src/main/java/de/ellpeck/actuallyadditions/mod/booklet/entry/BookletEntry.java index 591f675f2..74e5f3a89 100644 --- a/src/main/java/de/ellpeck/actuallyadditions/mod/booklet/entry/BookletEntry.java +++ b/src/main/java/de/ellpeck/actuallyadditions/mod/booklet/entry/BookletEntry.java @@ -13,13 +13,18 @@ package de.ellpeck.actuallyadditions.mod.booklet.entry; import de.ellpeck.actuallyadditions.api.ActuallyAdditionsAPI; import de.ellpeck.actuallyadditions.api.booklet.IBookletChapter; import de.ellpeck.actuallyadditions.api.booklet.IBookletEntry; +import de.ellpeck.actuallyadditions.api.booklet.IBookletPage; import de.ellpeck.actuallyadditions.api.booklet.internal.GuiBookletBase; import de.ellpeck.actuallyadditions.mod.util.ModUtil; import de.ellpeck.actuallyadditions.mod.util.StringUtil; +import net.minecraft.client.Minecraft; +import net.minecraft.item.ItemStack; import net.minecraft.util.text.TextFormatting; +import net.minecraftforge.fluids.FluidStack; import java.util.ArrayList; import java.util.List; +import java.util.Locale; public class BookletEntry implements IBookletEntry{ @@ -60,8 +65,64 @@ public class BookletEntry implements IBookletEntry{ } @Override - public List getChaptersForGuiDisplaying(GuiBookletBase gui, String searchBarText){ - return this.getAllChapters(); + public List getChaptersForDisplay(String searchBarText){ + if(searchBarText != null && !searchBarText.isEmpty()){ + String search = searchBarText.toLowerCase(Locale.ROOT); + + List fittingChapters = new ArrayList(); + for(IBookletChapter chapter : this.getAllChapters()){ + if(chapter.getLocalizedName().toLowerCase(Locale.ROOT).contains(search)){ + fittingChapters.add(chapter); + } + else{ + for(IBookletPage page : chapter.getAllPages()){ + if(fitsFilter(page, search)){ + fittingChapters.add(chapter); + break; + } + } + } + } + + return fittingChapters; + } + else{ + return this.getAllChapters(); + } + } + + private static boolean fitsFilter(IBookletPage page, String searchBarText){ + Minecraft mc = Minecraft.getMinecraft(); + + List items = new ArrayList(); + page.getItemStacksForPage(items); + if(!items.isEmpty()){ + for(ItemStack stack : items){ + if(stack != null){ + List tooltip = stack.getTooltip(mc.thePlayer, mc.gameSettings.advancedItemTooltips); + for(String strg : tooltip){ + if(strg != null && strg.toLowerCase(Locale.ROOT).contains(searchBarText)){ + return true; + } + } + } + } + } + + List fluids = new ArrayList(); + page.getFluidStacksForPage(fluids); + if(!fluids.isEmpty()){ + for(FluidStack stack : fluids){ + if(stack != null){ + String strg = stack.getLocalizedName(); + if(strg != null && strg.toLowerCase(Locale.ROOT).contains(searchBarText)){ + return true; + } + } + } + } + + return false; } public BookletEntry setImportant(){ diff --git a/src/main/java/de/ellpeck/actuallyadditions/mod/booklet/gui/GuiBooklet.java b/src/main/java/de/ellpeck/actuallyadditions/mod/booklet/gui/GuiBooklet.java index 4a19654e0..e502be321 100644 --- a/src/main/java/de/ellpeck/actuallyadditions/mod/booklet/gui/GuiBooklet.java +++ b/src/main/java/de/ellpeck/actuallyadditions/mod/booklet/gui/GuiBooklet.java @@ -10,14 +10,17 @@ package de.ellpeck.actuallyadditions.mod.booklet.gui; +import de.ellpeck.actuallyadditions.api.ActuallyAdditionsAPI; import de.ellpeck.actuallyadditions.api.booklet.internal.GuiBookletBase; import de.ellpeck.actuallyadditions.mod.inventory.gui.TexturedButton; import de.ellpeck.actuallyadditions.mod.util.AssetUtil; import de.ellpeck.actuallyadditions.mod.util.StringUtil; import net.minecraft.client.gui.GuiButton; import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.gui.GuiTextField; import net.minecraft.client.renderer.GlStateManager; import net.minecraft.util.ResourceLocation; +import net.minecraft.util.text.TextFormatting; import net.minecraftforge.fml.relauncher.Side; import net.minecraftforge.fml.relauncher.SideOnly; import org.lwjgl.input.Keyboard; @@ -39,6 +42,8 @@ public abstract class GuiBooklet extends GuiBookletBase{ private GuiButton buttonRight; private GuiButton buttonBack; + public GuiTextField searchField; + protected int xSize; protected int ySize; protected int guiLeft; @@ -73,6 +78,12 @@ public abstract class GuiBooklet extends GuiBookletBase{ this.buttonBack = new TexturedButton(RES_LOC_GADGETS, -2002, this.guiLeft-15, this.guiTop-3, 36, 54, 18, 10); this.buttonList.add(this.buttonBack); } + + if(this.hasSearchBar()){ + this.searchField = new GuiTextField(-420, this.fontRendererObj, this.guiLeft+this.xSize+2, this.guiTop+this.ySize-40+2, 64, 12); + this.searchField.setMaxStringLength(50); + this.searchField.setEnableBackgroundDrawing(false); + } } @Override @@ -81,9 +92,43 @@ public abstract class GuiBooklet extends GuiBookletBase{ this.mc.getTextureManager().bindTexture(RES_LOC_GUI); drawModalRectWithCustomSizedTexture(this.guiLeft, this.guiTop, 0, 0, this.xSize, this.ySize, 512, 512); + if(this.hasSearchBar()){ + this.mc.getTextureManager().bindTexture(RES_LOC_GADGETS); + this.drawTexturedModalRect(this.guiLeft+this.xSize, this.guiTop+this.ySize-40, 188, 0, 68, 14); + + boolean unicodeBefore = this.fontRendererObj.getUnicodeFlag(); + this.fontRendererObj.setUnicodeFlag(true); + + if(!this.searchField.isFocused() && (this.searchField.getText() == null || this.searchField.getText().isEmpty())){ + this.fontRendererObj.drawString(TextFormatting.ITALIC+"Click to search...", this.guiLeft+this.xSize+2, this.guiTop+this.ySize-40+2, 0xFFFFFF, false); + } + + this.searchField.drawTextBox(); + + this.fontRendererObj.setUnicodeFlag(unicodeBefore); + } + super.drawScreen(mouseX, mouseY, partialTicks); } + @Override + protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws IOException{ + super.mouseClicked(mouseX, mouseY, mouseButton); + + if(this.hasSearchBar()){ + this.searchField.mouseClicked(mouseX, mouseY, mouseButton); + } + } + + @Override + public void updateScreen(){ + super.updateScreen(); + + if(this.hasSearchBar()){ + this.searchField.updateCursorCounter(); + } + } + @Override public boolean doesGuiPauseGame(){ return false; @@ -113,6 +158,15 @@ public abstract class GuiBooklet extends GuiBookletBase{ } + public boolean hasSearchBar(){ + return true; + } + + public void onSearchBarChanged(String searchBarText){ + GuiBookletBase parent = !(this instanceof GuiEntry) ? this : this.parentPage; + this.mc.displayGuiScreen(new GuiEntry(this.previousScreen, parent, ActuallyAdditionsAPI.allAndSearch, 0, searchBarText, true)); + } + @Override protected void actionPerformed(GuiButton button) throws IOException{ if(this.hasPageLeftButton() && button == this.buttonLeft){ @@ -130,12 +184,16 @@ public abstract class GuiBooklet extends GuiBookletBase{ } @Override - protected void keyTyped(char typedChar, int keyCode) throws IOException{ - if(this.previousScreen != null && keyCode == Keyboard.KEY_ESCAPE){ + protected void keyTyped(char typedChar, int key) throws IOException{ + if(key == Keyboard.KEY_ESCAPE || (key == this.mc.gameSettings.keyBindInventory.getKeyCode() && (!this.hasSearchBar() || !this.searchField.isFocused()))){ this.mc.displayGuiScreen(this.previousScreen); } + else if(this.hasSearchBar() & this.searchField.isFocused()){ + this.searchField.textboxKeyTyped(typedChar, key); + this.onSearchBarChanged(this.searchField.getText()); + } else{ - super.keyTyped(typedChar, keyCode); + super.keyTyped(typedChar, key); } } diff --git a/src/main/java/de/ellpeck/actuallyadditions/mod/booklet/gui/GuiEntry.java b/src/main/java/de/ellpeck/actuallyadditions/mod/booklet/gui/GuiEntry.java index 9a731f480..5ac48ce75 100644 --- a/src/main/java/de/ellpeck/actuallyadditions/mod/booklet/gui/GuiEntry.java +++ b/src/main/java/de/ellpeck/actuallyadditions/mod/booklet/gui/GuiEntry.java @@ -32,20 +32,24 @@ public class GuiEntry extends GuiBooklet{ private final int entryPage; private final IBookletEntry entry; private final List chapters; + private final String searchText; + private final boolean focusSearch; - public GuiEntry(GuiScreen previousScreen, GuiBookletBase parentPage, IBookletEntry entry, int entryPage){ + public GuiEntry(GuiScreen previousScreen, GuiBookletBase parentPage, IBookletEntry entry, int entryPage, String search, boolean focusSearch){ super(previousScreen, parentPage); this.entryPage = entryPage; this.entry = entry; - this.chapters = entry.getChaptersForGuiDisplaying(this, null /*TODO Insert search bar text here*/); + this.searchText = search; + this.focusSearch = focusSearch; + this.chapters = entry.getChaptersForDisplay(search); } - public GuiEntry(GuiScreen previousScreen, GuiBookletBase parentPage, IBookletEntry entry, IBookletChapter chapterForPageCalc){ - this(previousScreen, parentPage, entry, calcEntryPage(entry, chapterForPageCalc)); + public GuiEntry(GuiScreen previousScreen, GuiBookletBase parentPage, IBookletEntry entry, IBookletChapter chapterForPageCalc, String search, boolean focusSearch){ + this(previousScreen, parentPage, entry, calcEntryPage(entry, chapterForPageCalc, search), search, focusSearch); } - private static int calcEntryPage(IBookletEntry entry, IBookletChapter chapterForPageCalc){ - int index = entry.getAllChapters().indexOf(chapterForPageCalc); + private static int calcEntryPage(IBookletEntry entry, IBookletChapter chapterForPageCalc, String search){ + int index = entry.getChaptersForDisplay(search).indexOf(chapterForPageCalc); return index/(BUTTONS_PER_PAGE*2); } @@ -53,6 +57,13 @@ public class GuiEntry extends GuiBooklet{ public void initGui(){ super.initGui(); + if(this.hasSearchBar() && this.searchText != null){ + this.searchField.setText(this.searchText); + if(this.focusSearch){ + this.searchField.setFocused(true); + } + } + int idOffset = this.entryPage*(BUTTONS_PER_PAGE*2); for(int x = 0; x < 2; x++){ for(int y = 0; y < BUTTONS_PER_PAGE; y++){ @@ -100,7 +111,7 @@ public class GuiEntry extends GuiBooklet{ @Override public void onPageLeftButtonPressed(){ - this.mc.displayGuiScreen(new GuiEntry(this.previousScreen, this.parentPage, this.entry, this.entryPage-1)); + this.mc.displayGuiScreen(new GuiEntry(this.previousScreen, this.parentPage, this.entry, this.entryPage-1, this.searchText, this.searchField.isFocused())); } @Override @@ -108,7 +119,7 @@ public class GuiEntry extends GuiBooklet{ if(!this.chapters.isEmpty()){ IBookletChapter lastChap = this.chapters.get(this.chapters.size()-1); if(lastChap != null){ - int lastPage = calcEntryPage(this.entry, lastChap); + int lastPage = calcEntryPage(this.entry, lastChap, this.searchText); return this.entryPage < lastPage; } } @@ -117,7 +128,7 @@ public class GuiEntry extends GuiBooklet{ @Override public void onPageRightButtonPressed(){ - this.mc.displayGuiScreen(new GuiEntry(this.previousScreen, this.parentPage, this.entry, this.entryPage+1)); + this.mc.displayGuiScreen(new GuiEntry(this.previousScreen, this.parentPage, this.entry, this.entryPage+1, this.searchText, this.searchField.isFocused())); } @Override diff --git a/src/main/java/de/ellpeck/actuallyadditions/mod/booklet/gui/GuiMainPage.java b/src/main/java/de/ellpeck/actuallyadditions/mod/booklet/gui/GuiMainPage.java index 315622cef..2676c7735 100644 --- a/src/main/java/de/ellpeck/actuallyadditions/mod/booklet/gui/GuiMainPage.java +++ b/src/main/java/de/ellpeck/actuallyadditions/mod/booklet/gui/GuiMainPage.java @@ -49,7 +49,7 @@ public class GuiMainPage extends GuiBooklet{ if(ActuallyAdditionsAPI.BOOKLET_ENTRIES.size() > button.id){ IBookletEntry entry = ActuallyAdditionsAPI.BOOKLET_ENTRIES.get(button.id); if(entry != null){ - this.mc.displayGuiScreen(new GuiEntry(this.previousScreen, this, entry, 0)); + this.mc.displayGuiScreen(new GuiEntry(this.previousScreen, this, entry, 0, "", false)); } } } diff --git a/src/main/java/de/ellpeck/actuallyadditions/mod/booklet/misc/BookletUtils.java b/src/main/java/de/ellpeck/actuallyadditions/mod/booklet/misc/BookletUtils.java index 83bab9043..bc299d41d 100644 --- a/src/main/java/de/ellpeck/actuallyadditions/mod/booklet/misc/BookletUtils.java +++ b/src/main/java/de/ellpeck/actuallyadditions/mod/booklet/misc/BookletUtils.java @@ -46,7 +46,7 @@ public final class BookletUtils{ GuiMainPage mainPage = new GuiMainPage(previousScreen); IBookletChapter chapter = page.getChapter(); - GuiEntry entry = new GuiEntry(previousScreen, mainPage, chapter.getEntry(), chapter); + GuiEntry entry = new GuiEntry(previousScreen, mainPage, chapter.getEntry(), chapter, "", false); return createPageGui(previousScreen, entry, page); } diff --git a/src/main/resources/assets/actuallyadditions/textures/gui/booklet/guiBookletGadgets.png b/src/main/resources/assets/actuallyadditions/textures/gui/booklet/guiBookletGadgets.png index 0bfed6f38a287004c8c060f1413d39a4d4dcc7df..a5842423facaaca1f7a2e1ba88e0199b13ad8c48 100644 GIT binary patch literal 5822 zcmeHL`9D-|`@hFbwrrJ;HHJ3z$(nuN5-QP3*~u1T8*9cG36(6RkR=ptwk#RDu_tR$ zLuhC)Wym_VF+7K!zR&0N{P6q*-yhEF+~>Zp<$YcEeZ9}Q&Rw%}hMep|>;M2xW22K7 z0DwV57+__FZdSgpoS_@SMb|_Zfa3Vw8}_@PXEtx6t7rhYUTj~m=;7C1&?JkWv55f- ziB)uus@zQ3@i}Np(9gia@1mEpU!c9O6Y#co^YoMUbn+8Zkd~E}JE4zop9X;MsPReN zOF=d>!`7YqiC0;?&t#k3-OauW!EfgJ@C{PTv+^iwn=w_#?f&Jq;yyKh*9*ex$nNCC zBlE%%1!NV^`ysHkqp*i6K4c^D4+eLP4+`^h82RK}I_Vw$jQ#ID$M$+hAYA1;<+WUv zI~O-yTwExfPfA@j4c8|^SDwhb1UOUHX6u%z`IV!$b|2BIUHWw9s<~G!jV~WRfzH6> zXSZaze@!YPj=Esi7Vf{e2ZA=&DCWVxDkyxU3YL z2-AF0=GDw9OxSW>{Y6?kZN&+>)mb*ssS7--1?ukYFL5Wp_`y1blA63T@@E9zlQlz@AH~)) zk{E}BC5OiM;cho~XQ)f0)e(T0WG*4{|9?s}WzsYKkggt+&_WAhV$)d7t63Ndw| zb{8D)Tz)n$qfT1AU`soQ!!W&#^-2q2Q0fyzv^KoULwh!-!|BNPUu?>?LKeHwgMoEu zjgap{@#cCuw<2IV{sKU?EJ_DGq(@~hwPqqX={$pxx3_e zW=o{Hy32RPzY^W6R`uojWrE7IocS&s=6-T(7$qa{jd(&m`HV`%?5gMD8I$GOsW*8L zHiN&wo2wrBKh^~BO~pMZkE94uI+UT?wQg zrCY#A>&ws!{_-*FPa6xbb(zBJ+4u9oahRh_I$E0@hC$+6uUwjt4{XmsJ_4-gb>Ya&<*W@jZ<| z3<>cL!$WE97sor>*iOY z?eRt((T?gm)OzN(pqxYTPH_sGPeV3+ed!x23^7KPyoFR`;%A-(X%shv(~W0 zy=d?$~wt2U42o^X&CX_>p2E0jYQfwd!XZske_BsVVjtyOH zh%PO#Ec&2Tww(0cacdbxydGV*W)?8n`T79wbPaOrUF;wW_E+Ly3f%7e4-35>{R>>6 ze?tjyiE3D2{)PfOk097?H7S5$o&hwuYj7lxWPKZ_!065(x*)&Jc@M6Be{62kURbw2 z<VB^rJ=^n5Uqph@4z%8_>DK{$z>7T_&!U#Bcm{APnK!lh27)G*$(-6)J=gNd?ttHyh=T=eKG)gYYi9Ek+6S46()96z;B`n$2 zgGB2eKd!8){-FL#V%tIwCU*=X-S7+vQb!w>p0cHldz&N&5NhW{YGu~U5jay0oNPMf z^9rtQR?pV<$Bw*$N2IM?7Ev1b#oZPhzTEZd7lF;?|;?TV-TNuBO&J?MP9;@u(H z)xYDQq^Ti^MR)I;wY9To^c3IPcJF2naya>_?LbZ>EJ4X)`W7sKk6Gs{q(;#T6e=!A z96D?E1 zMT>?dlz-PQU)x&I-g0wp8k~F-oRp#Mo@QfMu{e>(8D35`tA`+?Q@3UwqEt+J`p{|R z)k{{vl0Canr9(6!|6xQVV;xfk1y-r&-T-ttpp6)TMVnTY1?6MOZ$GjMtRMhpi3ET#wOl|Ex>5 zbj6DJmGp~lCoDJtjl7!3nCpF)fHEsHd0*#hk~5?BBj-I5%KG13{Y%o8{7j`xS+Cb< zazlj?=k9Glt_nO34Go)!+X*k#s)tqEt-Q{At|(a@KQxRY>Pi@FZl_h@0ctaF#YU@i z%me*FqoX_XU4fg_+711z|1g%sh=!s$t{<1DlqI>@X|E0U1 z9H7)5|7L(9*9NwECFP7MRCg&o!91=)Y0ztJdaRpDhK6b5m1Wi}4;X)>wNF!>^T+kY=k}x{cv0@;Lk{#o{kE(&OOyQP@{KDt=;dtrM;pT|Tb*{HA4g zw}?n}L2uE%g2{=4PX*^d(i0YZrJg1-6QfCSQd@DtWVpV|g4s3)3swD?L$r^!_(9xQ3Pf2w%YvL7qAlb@Mp%p1-0a6%Ox~M5l9eW`!e zYw8nRL-th{{UEn~ip_6d4xSvy>a}kG7WW0cL)j1B13Up+7G9FfI)A~%IL{++Bj$*z z;_F#A3;1n<#6!rj%BO$D!~Ph%Rh!_dolT)JFw^Rlg1?jm*Ng9$9HF1)Va0d<+oEGH z=tZBrLwO$djhH=(9aer`Qs4S#?JejXD#-SfROS@`UC?d|90E&9=90=uw{gPkrLus} zk=ZJlYb5;&-*|7 zZ1>>ngTSW3oNTjNslJrC;Jt2djb;W~sd`aG5SeF6JYbF~zM!m@E{|XaQpYXsKd$;X zmM(r@9E?}ex-`OqHUzTKa|N^ROn=1adj8@~M2L}lN_QiB3hqLDE_Zq?mEe%y9~q90 zB7nIj4oFK5FkR$l?iOT%6S(C^=N+2?>sf076&KdxUAX+d$5%V3s!DRteLD*~t0q#l z25I!`%o=svZv~F~+mEJ;uU72Y=>uz?I_{INu_a<2xvwiUE6#KBMMp_D*DPHVZ~jsd zs?g1&9D<~LY@UYW-Sn;Yk?iD{e>f<7bicjVlDzy;MSplYG~IBYi+06`;*PRoxU`W- zt(Q@;q9-y|zPamLTd=};dZX$W9%8ey#eKG_qp9B{$_9k+@vVa2ooC4!sW`@%hTL!6 z2E$IefCS z!`hI;#{?76lJX>m3D0@>KdRgcFF6AXb~KG&XeY~C-q?}hZROpeAuMzFL@7s0cACB)?5$c}Au*T2EXrALg;b19&wCO3n zEBLL*UF(BlT7N!_!nK3J>_P>ukr*oky=mue#+h`^VXYU>7Y-t(x&MT4nw{Tlwd%a% zf0=j{I=0{aj{A3E$*6~oyLjt7`v<-)` zWaoIpB8!AS6V;aUqQ_|a(^Afh_PzEOfli1VP;&rh+vnavKL-}44?HG*kYLs^hU0DjFN<>4MQP;p<;gpS{%M#bFFXA^!0l+KtKS0WYqY4%rkucfAP$!)Y zVd*yMg+6LlB4KX@?RDE_=d(ck->k zJMOdp4!)d#Rsryc*1X9fQf58q zB#Pj`?h&!v4h6P*hLWfydS3W#3KKIpFK=;H_dYBOFZCu~(Jj4ZBY!_R*7bM$hVRFy zyo{^%vb-sEp!v-66c~4JlpDU9>q+4pDtWNYyc@I;G&t|5ittd+l(={ z$}3@!vnB^#Y`4lP4F|g7VX{ul8d@!Ed~Vq8SQ511p-_}eF}%v3{k-E^x1AjGeDia3 ze=JOgRr12{rM?DLAvli5BB$c@iwLyBOI^C?Z>tqjd%?+V5dkp18@lQ30`McDZ;gC* z?*Za*x=j%V_bf?WKMP3|AJ-sgnp!G1@=j4RISB~~uirQwIHGnFdChZs<+=EXV%m>T zx8Q~YEnR`~Rn)FCMYdqO1dBPT!1dPXBpLkw;6F)lOek0jbCVO-*KUi7kX;O+=t|N?vW3vKZ&@--WJ}1cr0iL`kuCc+)Y!^e zc0-oI*s>00%;5V*_j~W>@%Vnf|G@W$dB4y5oabvjuh;9m&O}_jqQlC}&kO)yy`-ys z4FDK4gaJkb^fGn-%NBak+nv`x4?tlI3+?U!=$pw^_qGQB2eWp6u&|-`F3=={=Oz6M z3?q!f9Lh33i%%~>Q+%Emj6JWr*m`>3b-xE(@7{OzJmY-NQ&jGZ^ck76TJ#R{0B|*2 z(msE~$NcAz#RtJYGX__^w5t&e%oiAiuX5$X6GV= zPIz@?Jos50{j*$V0X|{C^sGKYK#)eM>YHiV%2nM&7A9r4jHJn%A{i^=CaT`}Lf zqMF_M{9=Heo!wOH>ms6m3~A;EH9^+yfh}=kxTd{k*mc;ePv75fCiB%3><_;=Nn@85 zqDoicqssGr#gbSAP8>c;A*NUmD~QRb)u{7D&sT{ZIOV*mg~}Xe;9I!)j03OI1(On$ zCpfU@p8^?=cm`?%1J~?_A1>BBM^o4JA1wY*8>se`g=0jqh~;d$(e`tTiSxr%-gz^N z<%H#H=(wyk+IE&}zFHseA{h`! z5IKwl*|%*Zv5=58ouzlE)(_O?_X0(G&ZuZ-V9~pZhfLAHH@d z`m9AV=fQ<@SUm08b99*#z7dFq=bx|62G0VUwZu{dMigpuTTnsjbXSG;c1~lPs?H3{ z$y=U$g_%%4u%h!fPeE0m0$Ll2a!*u|e!dFp?El((x-;#p5%5=?^!NuuG0y(@r3ZQRpVT&nWCtp}PQyh-MTkB1NFSO4FIYYWuiKWy&@QHI z!9^*?E(zxSq28BPTizj<=(kU~8Y96<8nlgMDUq=dnqfzlenR%*WRlr-$VfnK_=m}A z}Cza?+d3h&n4Q}ngr}dn9Z|sKs z=I5w3RKD12Sd77aad_Pc5yh;h{k^Xb`oO5t4mc-$UN8ZNTQb?!vGj)(p@y)GaEw2c zA8R=c6Zk-j7ct}C)w!P6Qm)8UC{6a3w2UO7(d7DL2o1eiUe_x^2tc`d03#>8(hz2z zGPLv3#Q^I$>rM%u5~PjiJa;NXH3EAkL56m%YfQjPRVgg=I)vUN#?&bE^|J{EqTT07 zlPhy<+g}&CdSjozI&Putk@-PSd}w3T;ki&5ItkW!t*vm>GzzAHdI4J+Bw1tSisaUL zurw1s#7a%|96oDjpv~^oN=<8Qqxw=^+3^kG3Z>gr(jg=!uCwrQa@YhHXo?Y$dYpGX z&_kV07c`&4`g6Jp>_DN)MeI;FOH}o$CeK8L<*@rK<-BU4a3ad)DLIaC@U!a`Dl|?H z=n_)Y9+uX#FXbaKM)|a99k7wUUAv8Ed%{K=%IB_fpO2g%J;nI0oYtMDLxol-|{db-VapA5y=^_&2Ba%n~2_^W`N{xTPXBRBWkM? z2kn39xK`hYS-|{A$!gqqw~+z2QJ(23txxULFC#;y{-eae+-7A^soBKoC;X>7NS!K! zxsKOItXzj_)y13z+v^XDfUDMMT@p2tx>#F_gx%j%d+02Ms-78xa`SY4^|kr_!t{Rk zmJ}IalC8|0n3Ks5zW1+N*FKBcUJ;k4GGS8aX(!Ho^3B84uY~}v(n|s>i}LCGphqAyi!p+>v)%r5KJ{8aOqXx z!v&!cr-1igu;;vYj=@upc_EeFUHJ+VZIfhO4B&Ky*I5CQ(E$uiOPCopOfIE3nXPls zI8xQ@(B+*N8nE_??O6kH=%IR-^M@#yqHMK>$Av{I7Fs)cBSppjWwgs0Kl~iKv4& z1(K?0R((I-9Z$i|4MfoF)F|fLtw#Lj!p9irRVgN@b^Ib_hk?EK6au5s^gey#*v?n5 z6aixoU9AokULvEX=OU-k_PhoC3~)@`qr9R_;&)feLN)}D%$YL5RmdTY=YwGyPJhiI ziJkikCHL}VhA!fQ8c@7II|t59wFK3E=E@2!`ClgzHsxbEW|g{N9kNEd;zcEqnBq@XBG6MBd`Cp0q336yDXMF1_{6gvA6I#ty5<2dYIz6Z|PmiWX1a0008M!nOywA?6$Vk2}?*4-Wr?b8?r;g}b)BJjbz&o@x84gt7AnvP6?Sp!QX*Ro zZ&`|O2^H6dO!b{Vq4!e;a&Q7e#d<7j1DnAx^8n%k{_%Wdul8VG@RcXDU3q!0-~6~Y zr)g;j|c8 z8{&Ny(F@+Ezq`H}woSM!oarpk5s=&G(N{39;H~C+#;NbTjXL^mH!ED!sYmDju%p+I zZ3(qGo$A%MMtbAa>-Gipi&r*qO>pAC$1k9!9jIz1xXg`0>dfLdtd|WIWjGrOdsbS} zSkzV;o;*UIJEln$wI8`ainykDg@9Di6tYtp^_|MTOvbiGGO%@Wo776CblCAtD;FQ@ z;OoINdo4`&JB0)!*{ey3EVp793R+dm+YZlC$P;tQ`QIz@$LF-?4zL1Q9_KP%;6we` zis|S&fzaR&V!*s+BSH=Q53Gdj=qf1!_AJzpu5KPqjPkS|cr;sCdJvu}AUnFr)o2g1 zT)(5}l1lsyw}o{d`y4VpSmJO@c52r41HPjHOgAFSQ6Vya07kWrjn(^qOjH8)k8NAq zTN@)%5@qeb*2lI&wDAf3=mvJhIAKMGop=RkbOM`$*wV_)e(h_)DBTQ31Gp=jC-Flp?NLnpioUkp&+e|Os zyCFYd6r@q^^*2XDxJCzuSaQs^uSHl8)otWy#g_cvYv1TJT*?|N3x}>+0y!vs@4f(? zc+v^>)oNb}-f(hs{X4S|iyNzvRn#zd-kAf8(51Xg*ZS zQ3gj?xvevuJ(cW`NMLfafcLalI!@WTSqw~6u(qiN`p|gOJeHiM9q1wpuPLe|$|9(m zYht)Q=q8b3CxS}8b4Ye{+4{w2`9}@L?^)o)r;lKxVSGzu87n}abp{FH5f~BMIzm%8 zj6J?m+O?h6iUr($Csh>J=izA8I3z*&kaI zOo$)bs=T68l4W3RdT7Mb&2d^&LNEWYreoAOZNgKxA~rW+{Z^D~flS98DC4{?)}GGPFVcQ!pH^Y|v$5X-ua(1)7{A@q1a$bS{k0-Q;<?bSnlcpMDa~GqWl4h3tqV-#s0?gi}n-oP1FgSPp2V$)x8cmYk zlap<&Qi4r4*)1FfNDLnz7Had!s#M*{l9dQp=OMV|MFc!ma4+IW^&md>6A))m4S326 z)MS9IO}!v!AJk%l!Z_1a-%^GPQTOy|JRI|#J~B-jT9yXla>mFInDlSZQJ)4WxGN!t z3#^z@&vx+S8QiV25??>gyBnKWvJ%@b;b@ag;_qww+>i(mf{0AZ-~{b%eykSIRc1du z!QPy^F#3zfg|*UFWgKzlx!Nr#71mB7jgbva`rXh4%HP6Y9(PLqFkF_RR$Bzv?)AQO z8s!dh->96DA30%`(J*57rIX-s)fJ%{HpAyHnSh6&65Az7XeKU;_=me%Xld(eYWas7 zAl~{@#haMbKdca{*vu)MvQzn7$!*2Bx$|zfD*!umb68=`PlhL^%K%0uPGFycCCL-dB&U=J<57)%O>vX2)|CzperIw~5SX>+#X_YQ2~|9}yP~aC>7ek9`h7O&!jptWThG2b zapu9d4na!!E4;0`1x1I68l(%$x5r}9=Lx|O2%ji6O}MTC5r@9<)ZHY%I0!LKJ1+Dm z3Y7`xju2-n(AD{6ya9 U{i}P>zn5nwCNT1z~?J5dZ)H