From 3168c39ef1e445db1a6e22a7ba63e8cb4714e6e8 Mon Sep 17 00:00:00 2001 From: Mavridis Philippe Date: Sun, 12 Dec 2021 16:24:28 +0200 Subject: [PATCH] KMix: system tray icon enhancements * XDG-compliant icon names with volume level specification (low, medium, high) Low-Medium threshold: 33% Medium-High threshold: 67% * Improved built-in icon theme with distinct volume levels (according to above) * Choice between classic KMix icon theme, imrpoved KMix icon theme and system theme. * New KMixDockWidget::getAvgVolume() method Gets average volume in %. Code actually comes from KMixDockWidget::setVolumeTip(), but was put into a separate method to be reused for determination of icon according to the volume level --- kmix/appearanceconfig.ui | 130 +++++++++++------- kmix/kmix.kcfg | 17 +++ kmix/kmixdockwidget.cpp | 97 +++++++++++-- kmix/kmixdockwidget.h | 2 + kmix/pics/CMakeLists.txt | 6 +- kmix/pics/crystal/CMakeLists.txt | 13 ++ .../audio-volume-error.png} | Bin kmix/pics/crystal/audio-volume-high.png | Bin 0 -> 1581 bytes kmix/pics/crystal/audio-volume-low.png | Bin 0 -> 1379 bytes kmix/pics/crystal/audio-volume-medium.png | Bin 0 -> 1423 bytes kmix/pics/crystal/audio-volume-muted.png | Bin 0 -> 809 bytes kmix/pics/oldcrystal/CMakeLists.txt | 13 ++ kmix/pics/oldcrystal/audio-volume-error.png | Bin 0 -> 1176 bytes kmix/pics/oldcrystal/audio-volume-high.png | 1 + .../audio-volume-low.png} | Bin 1215 -> 1215 bytes kmix/pics/oldcrystal/audio-volume-medium.png | 1 + .../audio-volume-muted.png} | Bin 607 -> 607 bytes 17 files changed, 222 insertions(+), 58 deletions(-) create mode 100644 kmix/pics/crystal/CMakeLists.txt rename kmix/pics/{kmixdocked_error.png => crystal/audio-volume-error.png} (100%) create mode 100644 kmix/pics/crystal/audio-volume-high.png create mode 100644 kmix/pics/crystal/audio-volume-low.png create mode 100644 kmix/pics/crystal/audio-volume-medium.png create mode 100644 kmix/pics/crystal/audio-volume-muted.png create mode 100644 kmix/pics/oldcrystal/CMakeLists.txt create mode 100644 kmix/pics/oldcrystal/audio-volume-error.png create mode 120000 kmix/pics/oldcrystal/audio-volume-high.png rename kmix/pics/{kmixdocked.png => oldcrystal/audio-volume-low.png} (92%) create mode 120000 kmix/pics/oldcrystal/audio-volume-medium.png rename kmix/pics/{kmixdocked_mute.png => oldcrystal/audio-volume-muted.png} (80%) diff --git a/kmix/appearanceconfig.ui b/kmix/appearanceconfig.ui index a55f5a11..294b00fb 100644 --- a/kmix/appearanceconfig.ui +++ b/kmix/appearanceconfig.ui @@ -8,8 +8,8 @@ 0 0 - 464 - 280 + 487 + 278 @@ -19,30 +19,18 @@ unnamed - - - - Horizontal - - - - - Vertical - - + - kcfg_Orientation + kcfg_Menubar - - - 3 - 0 - 0 - 0 - + + Show &menu bar + + + true - + lblValueStyle @@ -50,20 +38,12 @@ Volume values: - + - lblOrientation - - - Sliders orientation: - - - - - kcfg_Tickmarks + kcfg_Labels - Show &tickmarks + Show &labels true @@ -71,27 +51,44 @@ - kcfg_Labels + kcfg_Tickmarks - Show &labels + Show &tickmarks true - + + + + None + + + + + Absolute + + + + + Relative + + - kcfg_Menubar - - - Show &menu bar + kcfg_ValueStyle - - true + + + 3 + 0 + 0 + 0 + - + spacer5 @@ -108,24 +105,63 @@ + + + lblOrientation + + + Sliders orientation: + + - None + Horizontal - Absolute + Vertical + + kcfg_Orientation + + + + 3 + 0 + 0 + 0 + + + + + + lblOrientation_2 + + + Icon theme: + + + - Relative + Crystal + + + + + Classic (Old Crystal) + + + + + System - kcfg_ValueStyle + kcfg_IconTheme diff --git a/kmix/kmix.kcfg b/kmix/kmix.kcfg index b6d595bd..d4ca0956 100644 --- a/kmix/kmix.kcfg +++ b/kmix/kmix.kcfg @@ -99,4 +99,21 @@ + + + + + + + + + + + + + + Crystal + + + \ No newline at end of file diff --git a/kmix/kmixdockwidget.cpp b/kmix/kmixdockwidget.cpp index 574a996c..fba30421 100644 --- a/kmix/kmixdockwidget.cpp +++ b/kmix/kmixdockwidget.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -47,6 +48,7 @@ #include "mixer.h" #include "mixdevicewidget.h" #include "kmixdockwidget.h" +#include "kmixsettings.h" #include "viewdockareapopup.h" KMixDockWidget::KMixDockWidget( Mixer *mixer, TQWidget *parent, const char *name, bool volumePopup, bool dockIconMuting ) @@ -72,6 +74,9 @@ KMixDockWidget::KMixDockWidget( Mixer *mixer, TQWidget *parent, const char *name createActions(); createMasterVolWidget(); connect(this, TQ_SIGNAL(quitSelected()), kapp, TQ_SLOT(quitExtended())); + + TDEGlobal::dirs()->addResourceDir("icons_crystal", locate("appdata", "pics/crystal/")); + TDEGlobal::dirs()->addResourceDir("icons_oldcrystal", locate("appdata", "pics/oldcrystal/")); } KMixDockWidget::~KMixDockWidget() @@ -254,6 +259,20 @@ void KMixDockWidget::handleNewMaster(bool defaultMaster, int soundcard_id, const } +long +KMixDockWidget::getAvgVolume() +{ + MixDevice *md = 0; + if ( _dockAreaPopup != 0 ) { + md = _dockAreaPopup->dockDevice(); + } + + if ( md == 0 || md->maxVolume() == 0 ) + return -1; + + return (md->getVolume().getAvgVolume(Volume::MMAIN)*100 )/( md->maxVolume() ); +} + void KMixDockWidget::setVolumeTip() { @@ -261,6 +280,7 @@ KMixDockWidget::setVolumeTip() if ( _dockAreaPopup != 0 ) { md = _dockAreaPopup->dockDevice(); } + TQString tip = ""; int newToolTipValue = 0; @@ -271,10 +291,7 @@ KMixDockWidget::setVolumeTip() } else { - long val = -1; - if ( md->maxVolume() != 0 ) { - val = (md->getVolume().getAvgVolume(Volume::MMAIN)*100 )/( md->maxVolume() ); - } + long val = getAvgVolume(); newToolTipValue = val + 10000*md->isMuted(); if ( _oldToolTipValue != newToolTipValue ) { tip = i18n( "Volume at %1%" ).arg( val ); @@ -316,7 +333,13 @@ KMixDockWidget::updatePixmap(bool force) } else { - newPixmapType = 'd'; + long avgVol = getAvgVolume(); + if ( avgVol <= 33 ) + newPixmapType = 'L'; + else if ( avgVol <= 67 ) + newPixmapType = 'M'; + else + newPixmapType = 'H'; } if (( newPixmapType != _oldPixmapType ) || (force == true)) { @@ -325,12 +348,23 @@ KMixDockWidget::updatePixmap(bool force) TQPixmap origpixmap; TQPixmap scaledpixmap; TQImage newIcon; + + TQStringList fallback; switch ( newPixmapType ) { - case 'e': origpixmap = isShown() ? loadSizedIcon( "kmixdocked_error", width() ) : loadIcon( "kmixdocked_error"); break; - case 'm': origpixmap = isShown() ? loadSizedIcon( "kmixdocked_mute" , width() ) : loadIcon( "kmixdocked_mute"); break; - case 'd': origpixmap = isShown() ? loadSizedIcon( "kmixdocked" , width() ) : loadIcon( "kmixdocked "); break; + case 'm': fallback << "audio-volume-muted" << "kmixdocked_mute"; break; + case 'L': fallback << "audio-volume-low" << "kmixdocked"; break; + case 'M': fallback << "audio-volume-medium" << "kmixdocked"; break; + case 'H': fallback << "audio-volume-high" << "kmixdocked"; break; } - newIcon = origpixmap; + + TQString icon = getIconPath(fallback); + if (icon.isNull()) + { + icon = getIconPath("audio-volume-error"); + } + + origpixmap = isShown() ? loadSizedIcon(icon, width()) : loadIcon(icon); + newIcon = origpixmap; if (isShown()) { newIcon = newIcon.smoothScale(width(), height()); } @@ -341,6 +375,51 @@ KMixDockWidget::updatePixmap(bool force) } } +TQString KMixDockWidget::getIconPath(TQStringList fallback) +{ + auto iconTheme = KMixSettings::iconTheme(); + + TQCString iconThemeName; + if (iconTheme != KMixSettings::EnumIconTheme::System) + { + switch (iconTheme) + { + case KMixSettings::EnumIconTheme::OldCrystal: + iconThemeName = "oldcrystal"; + break; + + default: + case KMixSettings::EnumIconTheme::Crystal: + iconThemeName = "crystal"; + break; + } + } + + for (TQStringList::iterator it = fallback.begin(); it != fallback.end(); ++it) + { + if (iconTheme == KMixSettings::EnumIconTheme::System) + { + TQString iconPath = kapp->iconLoader()->iconPath((*it), TDEIcon::Panel, true); + if (!iconPath.isNull()) + { + return iconPath; + } + } + + else + { + TQCString type = "icons_" + iconThemeName; + + TQString iconPath = TDEGlobal::dirs()->findResource(type, TQString("%1.png").arg(*it)); + if (!iconPath.isNull()) return iconPath; + + iconPath = TDEGlobal::dirs()->findResource(type, TQString("%1.svg").arg(*it)); + if (!iconPath.isNull()) return iconPath; + } + } + return TQString::null; +} + void KMixDockWidget::resizeEvent ( TQResizeEvent * ) { updatePixmap(true); diff --git a/kmix/kmixdockwidget.h b/kmix/kmixdockwidget.h index a7b420a0..8f119ace 100644 --- a/kmix/kmixdockwidget.h +++ b/kmix/kmixdockwidget.h @@ -70,6 +70,8 @@ class KMixDockWidget : public KSystemTray { void toggleMinimizeRestore(); void resizeEvent(TQResizeEvent *); void showEvent(TQShowEvent *); + long getAvgVolume(); + TQString getIconPath(TQStringList fallback); private: bool _playBeepOnVolumeChange; diff --git a/kmix/pics/CMakeLists.txt b/kmix/pics/CMakeLists.txt index 35ea82c5..92af67f8 100644 --- a/kmix/pics/CMakeLists.txt +++ b/kmix/pics/CMakeLists.txt @@ -16,8 +16,10 @@ install( FILES mix_microphone.png mix_midi.png mix_recmon.png mix_treble.png mix_unknown.png mix_volume.png mix_surround.png mix_video.png mix_headphone.png - mix_digital.png mix_ac97.png kmixdocked.png - kmixdocked_mute.png kmixdocked_error.png mix_record.png + mix_digital.png mix_ac97.png mix_record.png SpeakerFrontLeft.png SpeakerRearLeft.png SpeakerFrontRight.png SpeakerRearRight.png Listener.png DESTINATION ${DATA_INSTALL_DIR}/kmix/pics ) + +add_subdirectory(oldcrystal) +add_subdirectory(crystal) \ No newline at end of file diff --git a/kmix/pics/crystal/CMakeLists.txt b/kmix/pics/crystal/CMakeLists.txt new file mode 100644 index 00000000..71f61d26 --- /dev/null +++ b/kmix/pics/crystal/CMakeLists.txt @@ -0,0 +1,13 @@ +################################################################################ +# Copyright © 2024 Mavridis Philippe # +# # +# This file is released under the GNU GPL version 3 or later. # +# Improvements and feedback are welcome! # +################################################################################ + +set(theme "crystal") + +install(FILES audio-volume-error.png audio-volume-muted.png + audio-volume-low.png audio-volume-medium.png + audio-volume-high.png + DESTINATION ${DATA_INSTALL_DIR}/kmix/pics/${theme}) \ No newline at end of file diff --git a/kmix/pics/kmixdocked_error.png b/kmix/pics/crystal/audio-volume-error.png similarity index 100% rename from kmix/pics/kmixdocked_error.png rename to kmix/pics/crystal/audio-volume-error.png diff --git a/kmix/pics/crystal/audio-volume-high.png b/kmix/pics/crystal/audio-volume-high.png new file mode 100644 index 0000000000000000000000000000000000000000..bfa7234bf3caf1cd1d8a8ec03deafc5210372680 GIT binary patch literal 1581 zcmZ{kdpy&77{`Auxr`|!J4h}eNo^BHLv2jEu^2_DXrl-tS|xIgTrxtmip7+rrJP(w ztP5Kw%r(X6bQGF|D3X=)>-9SSoIlR%`+T1F>-%}WpMSo8JeeM392BAl0RR9>z+)*O z^?pf33G7^=n;3-RF^oF~03I;aWI=1dyAc&naR-1na{x$61Ay8Z>aW9Yxm^G{s!{lZF#w=;@t5Srb1ey=sT@micU2x&S6{2A z>oi1E1+8_|ATk4_;lGjxDgQ~|JQM)d00bv~?lj)-QsJWi>P9sV@aDV8N{kwz*UuMjTOJ6H(1m&-&pac2a*mwI zCF!TFKmyT`de83m8=rRk%IXX)?Y3gxo#;t6dd+~{ncvJ;21x-4qwoVvQA#YM&jYrmblZ&M7-v^B<>`C&RtWnv5U z5_SG>wn@FPx(_%YftnP-8iEnJyzt5I3o+9&n!}~5adr{ISi5WOwc8leub&L^{CoXu zY7^I5(v7WN35@8DEs@g=sM3s{32#~pL^yXG-7t8ic zDb7ITd!>DTyhA!Qbu=&#Aez38yg>DIpL4?`&@%&I%qJPib{;E5zFm_6Z7hE}?PdAd zGa4LQb!DC0#h=33;EpZD9ce-Mb&6?0&o7&idi*NtLWq7bcaQqqe6+@P)ADG_&O9Y2 z>VYe_xam*wAwQ!OwWt>~-Jz$}XS)_s+M?Y|^3j&AE-nf}SPtU(oDs&?i_(xn3S$!; zmE@Wlnv2VpmNc`IR8%{j+GsGmg-Z$GSF6b9AhuNE%?n^uu(d^_xfBJcs=ciTS_jow zQ!u-9gWxjUKBy#uRdtWqYg}+nKFQB{XMddcU|Tj^J=!3Hh9r2Wu(iBXammA`0ZLD` zJrDwBLWTz1qZnhM?nN<_4tUpIm40j8#}5#~Y+6rMR8-jKrbb8#Q#Oyo?+Sl??SIU(P1IUu?c)mp^`3@tB9hb3^Jab&!SjAG)kek zyOLbyFyalB8!a%e`TC=Mkbc%&21H$a_;Jp3`26w_gfNd-iQg;~j#RtCNKf9a3XP4N(pb2qC-9ytu>D z$0s8~qOQ-B-Ar|>PUzCXY_FEO-hrG*YD`@H=F;Ef`<)k;yG>A}|40A9MmQ!haSuNr zcVZ$WTq3NGsE0P^Osq)XEV|j+-$4vo23cy~9M)v~;PjEU7vB$+`>S(Lw*Sdfoi`j# z%XUOa?2^gzGn1qCagBO1j@djmplAXRVIo(v?L0@8`4mCCaIqyd$KEUIkZ$)v)S=>7 zRM!>f%TIR*C?k(Ba#eL9^5ru=DNqA$>k3)IO!>57{>JatDpxePpT9PJb;$aI05&LKmW+JU)@CS9umjDu zf;-VH*3~zb78DzdqMi%}0U+T>gf#*QmN&u{g|tQOw6TK2QE+(jt#On81S06P(2%qL UADCM?R0;+Ff-4z&&p9yVAJb67r~m)} literal 0 HcmV?d00001 diff --git a/kmix/pics/crystal/audio-volume-low.png b/kmix/pics/crystal/audio-volume-low.png new file mode 100644 index 0000000000000000000000000000000000000000..e42f7c8bdd217738ac511bcbbdd1e4186ffc9881 GIT binary patch literal 1379 zcmZ{ic}&x19L9geB1JHmp<@G`f}^0I+?!U-K&aeRYGHzcg|?J(6l}{`PPr-NDimmF zp;%5!xjK}itQN4-S&&O43Mj`Ih9OhIwaB`k$^O|NOWx=GXLQ`BzY``}Y~{ z0{~#ZyPFFdq~T}5_JB99eBcuZDACE=2>?2abv^}ag8mrJ4ebp8NpJv2&jJ7y7^VLX z0I}8p@CFM2h$;Xuh^guGIS&9DiYs0iq*|>`DlJXna+5inL?$z#NyvIIRdxR%SNfDS z{5Y0XNonj7JbvMY#&~#m1Ox=InmTGG<|2y912U^vBXbBxX9oudBogW8=NA?h#_1l6 zxhp;76J97^BxV$wnwr|%+dDfuV=x#3fsmY*k<&YY&8dWXQqEP$nith3reRbQyy>nVezxGv&+lN>+9=p-@e`4+|0|%D=#m4ZP!omWR$>eZ2(HxOYI8)a*{$$PI`Nl`it)rgplOdAT{{H@v zkr9PLF+M&%IXO8xI$BXtQCC+NStYUv&DFTz3(Mvklyw?a^ql0$eD5!lV(B80NGui) z3=Bvl5-yk9)YOFMh`$ZahM0Y?6_Ei;X2Ws?hl<lEXJLwXG0PM;{nI0hY)*BYcT1Z%$23Ei5doudnav=`l$W8V4ppzB&dRG=dyE zwf6?)2=|wZwbK0j{Ggy991ce$5_5BNTl;1ELODmViBPyLG$$n zl1imSu>yk1(6)DkCbnG@NQ6RRXJ_Zo(2zv_NI$Jj8y&0Z8Lpe!#uxX?WU|PfF%6$g zhzr@g`a!!)PVXDrL$1|9M?;(L){3sKo}s~o zSr2V6I)DpC#ew|hUul5U_`?WJ0_XXU29vLZfs>R0k`^USq2|c4E-v% zI%b}A;<%;BN%(2w6Q)e#)9?E6>ac>vM@K* zS@|sZjJ_im%pcb8 zCcJqT^`dlO0Jkcdqi=7#NPG2)UvXXU=-C3pEApb+Grh{GH9J4z7}r&AUdO;HaIh;@ zH61Y%jfL(iuHS1~$Pe$Q>v>}4(>6QWcE^{@p4Thax>v1&24%c6K|d$%6lG8VAijqF zt^mK(2QJ!_hP+0@2h#`$9EAV^um$&g%ElHv7#jzKtpmc|&cfOnVQrn!xMK33LR2)K Y6q@+|h0UEn9#{a}kti3@MeOZ=0LT@CbpQYW literal 0 HcmV?d00001 diff --git a/kmix/pics/crystal/audio-volume-medium.png b/kmix/pics/crystal/audio-volume-medium.png new file mode 100644 index 0000000000000000000000000000000000000000..4be127ebb77aa788b696cb87926b068d0068f4b0 GIT binary patch literal 1423 zcmZ{kc{tR09LIm;zSdD@m|SBbt7B#)V;bvdGzT+l5V6EKlLomW%8@40CDYJ^btg}V zCzCObh(zPaF}2AeI|zeN!p8o3p8aS4*ysCse_zk{{dwNs&-bryh7*;bte~v`0Dv-y zh<665{X=px;Qom0*at!?*p^}o0JXOj|E5cWXMKO7GX(%H8Ua9Z8USp9R`M(W#2^7+ z&KCf%xc~r($ZMqGz=n*U0|5{0{8)v}53@l>K7!~G1ptcaKO`Z#WkLc?*=Q2QURI=} z^piHUD##tY0Z{d)Q)5Bu{wqn4l46)q2>2jB65iI8*wxGqKXWp0ZbNZ zbM5%)qu%_v>GBKwq(0sZuKCWQ%pz9$^ZH8O`096Dt9AcsLYk1nr@!iuS>D+*ms*vXhW_uI2+uCIkOR2#mbP{)NH?l1 z87|Bp7k;p)9ldQh6g$i+tb_>Xri}~AS10!)>`L5l^s z-4Q6+h_6yKppH4StHP#uTiJV$J;~{bvpg2Npr1wbh01KLF9okJj^&njf8$Y$7uDUu zH}OfUlg#=0Mr{+VEG?fe+}Hw7O5KLU>CkFf8&PDQ)n{X|Slm$U0A)g%Z7ZVJpB5K` z;^Lr@Q}3uLrfrCNOYQT+1DkEjOe54;$f1%;B8Ah8%C3{DW^{omayW@C6uPZ!;98cZ z;&oe=h6V>yLpb308@b*0Qmu;E=;w#j-YoFsD}%`=x)e*hBnJ$bdQRT76Gqv#a<)(= z9gwvMUxp`V=)yWzjjs-yPE4v7ZvT0C+tR|O!u$-S0~t})uCh`(6w;3DSjCmH zIHfqE2`xD7`q#iz}F{!vx{chI8tfeF(K0cs@0|e(HTn)Lsy`Zy)x2S*C#IIcRUVF_82Lxxry`~dbNO8el#3X%EFh>;92v*rFC`c zZ)0j15BgROiCOthnIXg+@>sWFd#;%e1iNF`u;&%QQ>y|7pB4mV4H4YkAQz)9>4w6U&y`1r28s!DiQO$C13p_du@3mRrAl|QxeQqU z({GbbhkN##R%_@%JzMqI2ugfH@GTwnkHg^$R%Sg7oRld{Lv`j9ro#_)S-Tc$8N(4& zGbC-A48j#mz@OS+@4BLl<6e(pbstU$g(vPY0F z14ES>14Ba#1H&(%P{RubhEf9thF1v;3|2E37{m+a>X&O0cobU(rTGN ziow&xF~s8Z+$+}EQye9Ze!Tx?-}`;lo<3JrbhNBl;O5A}Eyya!Ey``*Ab;S{EhiW5 z>XYUI8@GrEa&zmqPP^CI5y%s_=!mq$;@z|FZuB@KwAs)wnCGxTecHLodB%#r{!ff% zQ=YlsROIxmItx*;rbGA7@0KuMahj!nE{}JU-jTI!SH4;$O^)^}Kk_WYO4{7&@!U4e z^4`e(I|SPwGj8wgjEb5i_o`IypYSbt{u>WAIUaw@QX05z!m`|rD_^aMeYg9`EIUi{ zn2ooj_7v95Tld7{PI~*^8J|vY#zqzKRleK2Ytc%{?MWHd)1^z!$f>TJwViKO=8Eha zsuNeNIKQ#7%I{iq>YA$)w#qF==r7ce{-9DGqgO8#SD_Vnap|Hry@}>x=P%Fwa6s5nGM)R%g#)Bl?sc`D-C{_oNoe=pttRwXGPm?l(9Tq8s)V8;H$NpatrE9}55HsQ0W~mqy85}Sb4q9e E0GZ`OJOBUy literal 0 HcmV?d00001 diff --git a/kmix/pics/oldcrystal/CMakeLists.txt b/kmix/pics/oldcrystal/CMakeLists.txt new file mode 100644 index 00000000..47b37e11 --- /dev/null +++ b/kmix/pics/oldcrystal/CMakeLists.txt @@ -0,0 +1,13 @@ +################################################################################ +# Copyright © 2024 Mavridis Philippe # +# # +# This file is released under the GNU GPL version 3 or later. # +# Improvements and feedback are welcome! # +################################################################################ + +set(theme "oldcrystal") + +install(FILES audio-volume-error.png audio-volume-muted.png + audio-volume-low.png audio-volume-medium.png + audio-volume-high.png + DESTINATION ${DATA_INSTALL_DIR}/kmix/pics/${theme}) \ No newline at end of file diff --git a/kmix/pics/oldcrystal/audio-volume-error.png b/kmix/pics/oldcrystal/audio-volume-error.png new file mode 100644 index 0000000000000000000000000000000000000000..b0497662696bfa8bcaf9e1274361daf9faf1ddd6 GIT binary patch literal 1176 zcmV;J1ZVq+P)Lyu# z()i5KgIRh0iFKctYP)X|92!ngP!N%kk?7A}pz>NT2MSAnekO2a)~Nyg*S)Os@ng-J zH3S3%ux;Bm_U+q8MPoY$>kY&m)5XjPx=0mg%>UNNyGM1ny1L@&>4~48AK~HQ#Ky*= z%{XkIjSRU`0zS;JtG{hYP3Xf`}S=nCMGberKN@a`}d>OYRS`= zv9szDJJR!~?~>=#Nk+T0k9np32D!ahtp90*t??SFtE(9v9%gK8j0Y+yDIqg6lf1k< z-l;aAO3Ck@7W`_)c~kP`zsdOp%6^B^-yr+S5bNKLps%lwk@|W}Cr)50EW}h)#CU!_ ze`RNLN342BdLeHd)(<@aRj2D>0#6z!`W24!jG?(Yiss6FGP(ze*cHp2bLXJ35n5ZJ zqXW9Spsfv>nxM9pPgP-fM8?W(?LN2dl*@rhuKJ;x%^a6joHWv!U)g{LWowOT&+R5q(Gk zW@SNQBAht`85xkC4hac}0ZZekDA>N8rd6x%zaR*qll`jR9b>gojq+Fx&zD@p@%_(O zQQe8y4pNokfXEdw_@ZuI{hKHou z;zGgN+H!tT1bYiY+kb+^)(NbW+n64L{sla4ljtrnhEOZ{4Y?rfd8(amp}k{ly_I}g z-~&H^3KG=qsrTiD?yHM+idi%%=Ws#%+(&(J)@;J8m;=C3t9#}nZ q@wn%Fhf%M&^ZU&muu;l%3iKbjob#4qQ)65J0000