pax_global_header00006660000000000000000000000064135543761460014530gustar00rootroot0000000000000052 comment=7a4ab7cd62dce5bed17f40ed147423fd0df9d118 gnome-firmware-3.34.0/000077500000000000000000000000001355437614600145365ustar00rootroot00000000000000gnome-firmware-3.34.0/COPYING000066400000000000000000000432541355437614600156010ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. gnome-firmware-3.34.0/MAINTAINERS000066400000000000000000000000451355437614600162320ustar00rootroot00000000000000Richard Hughes gnome-firmware-3.34.0/README.md000066400000000000000000000005041355437614600160140ustar00rootroot00000000000000GNOME Firmware ============== This application can: * Upgrade, Downgrade, & Reinstall firmware on devices supported by fwupd. * Unlock locked fwupd devices * Verify firmware on supported devices * Display all releases for a fwupd device Screenshots: ![devices](contrib/screenshot.png) ![releases](contrib/screenshot2.png) gnome-firmware-3.34.0/contrib/000077500000000000000000000000001355437614600161765ustar00rootroot00000000000000gnome-firmware-3.34.0/contrib/gnome-firmware.spec.in000066400000000000000000000025661355437614600224070ustar00rootroot00000000000000Name: gnome-firmware Version: #VERSION# Release: 0.#BUILD#%{?alphatag}%{?dist} Summary: GNOME Firmware License: GPLv2+ URL: https://gitlab.gnome.org/hughsie/gnome-firmware Source0: https://people.freedesktop.org/~hughsient/releases/%{name}-%{version}.tar.xz BuildRequires: gcc BuildRequires: meson BuildRequires: gettext BuildRequires: fwupd-devel >= 1.2.10 BuildRequires: gtk3-devel BuildRequires: libxmlb-devel BuildRequires: libappstream-glib-devel BuildRequires: libsoup-devel BuildRequires: desktop-file-utils BuildRequires: help2man # old version only used for prototyping Obsoletes: gnome-firmware-updater %description This application can update, reinstall and downgrade firmware on devices supported by fwupd. %prep %autosetup -p1 %build %meson -Dman=true %meson_build %install %meson_install %find_lang %name --with-gnome %check desktop-file-validate %{buildroot}%{_datadir}/applications/*.desktop %files -f %{name}.lang %doc README.md %license COPYING %{_bindir}/gnome-firmware %{_datadir}/applications/org.gnome.Firmware.desktop %{_datadir}/metainfo/org.gnome.Firmware.metainfo.xml %{_datadir}/man/man1/*.1.gz %{_datadir}/icons/hicolor/scalable/apps/org.gnome.Firmware.svg %{_datadir}/icons/hicolor/symbolic/apps/org.gnome.Firmware-symbolic.svg %changelog * #LONGDATE# Richard Hughes #VERSION#-0.#BUILD##ALPHATAG# - Update from GIT gnome-firmware-3.34.0/contrib/screenshot.png000066400000000000000000002771711355437614600211000ustar00rootroot00000000000000PNG  IHDR2hȎPsBIT|dtEXtSoftwaregnome-screenshot> IDATxwxTM%!R'TiQ*`+`WE EQC(AZG D<=wtfvDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDJ2*y]yH`V"%z#+հaZܰH]d&v=f%޽{EaIҲрg۶m=?/j+(p`%""""""""""RbWww{vVG뒓ǁ&'<*JZK7]vX-Ǐ[6""""""""""ib˳ r=pFO8gQˆyZBDDDDDDDDD*/7pZo jxN(xPJwHA~ YDDDDDDDDDDΖ|ϐONOO.N*""""""""""gkwr㻓ƦiZBDDDDDDDDD|`BvNmg8KcFaԣ0a f)\d"@99 Q#.8q c9xP,Q]ޝ|-N iJb?|LkHmr14pjp %L+3j7ep*9l=0sGDkgO]YaNs'W08,6В""""""""""ŀC x<2iL L&Eswhep3~>,a,"""""g݌>CG2rNgp*]nH!Um(Ӵ>HŸ2rhںv0lF^] JžPOÖoM=}PDDD!OtCU-^'J%EDDD!z9+W>㵊O՟> Xv*}ҐssfD60uf,ɜQ9W<0ɎVZ {>3/Vy{ظѧB9R%$Lydpq%Y';ӒEu(a,""""u%v.d7 ?}7[pEN ҹޒ\ٿ~Dd)r^߳X|-y^Dl,{qiՎVnV|[bYGK m[bߖʃvla67jߟKG6󯩛Yɋ7{\fNq׋!r;3SK~/ hѥ3-w_HsiH2rJk4<6-NEdէW9oב8(O;KyDDDD{:YK¤1`}23*J e)uU""""r:xl*;޶WGi/7ӗϳ. Kgg݇5?Gy{z r[`}%+-ڷs"ڷmSGwɞ>E&c ?=Ovml@)qW2WIs9l[/]Mq<6p5w?_ ?dWH ±e|-`i:>@5u:RԚ{_K XNq\豁qQ[tЃw$ϜE^\0M޸򰹀Ga\Њf1q;kHV0..۟,~'^Hs7HnJ#Iu gOY5#_5^eAA`Rtٱ1jd]7ӤE qW~Q'Rzm(~dgцaOC|yAX,Xppl+<ɦbkLɷf>&' 0/-n00X]Jrd*էFEDDDDα#6zgnzwB5O.IQ|M3.0%EDDDpIc5O'# w/C84wxE7&bcw£/r7Z`$OH,k7G۶gc6oˀQ8k[\#c LFVoa[R"%ϠE}P%g[>9d% Hͯ`i_DDD6+m n%7su5Oi,i9RXu# O=1,|4؆9l`s)4m b뾬mݵ2X:;< ~}3Cř|pbFdǹ yʲ4W@./}6,ɴa%,>El0O$_,""""R78fkeKV_B fo,>t5Wfm|s=p~hw]VfK_+.vX{݋-;qB^Tƌ:軯%#;S:g3-G{ǡM̝/}ĚY=>=GRV&vQ^c;]ɵHqrSsGDDD|S[ V ‹/Ka:~~D0@9/Ĺ=K2k>al*11NHq.o9w~ն򋈈39z)v u- O98 \HeXPK96+4 p= Ғ""""""""""{};w5(Yu{ ʲj,ȒNDDDDDDDDD,δe[};|4pZ<>P`%’ +K2)%EDDDDDDDDDj̈́Vdu+CkJ JJ J.Ns԰mbj;Cy{ӄI=:`DDDXT6tδ{Wc/2'qƠ6gM=Eg϶_,"""""""R 3f̓, ?mLcϭ*p~ޭ+=T3mOl`Clk:5_*,ބF7QhVL2q!V7/Ûкs_zE{EDDDDDDD|n7|q>|8iiiՌ'>>P7 0Z؜`gpNtɺ¯$d8npW=3,O%ܝ ԇifysHד^( ;[FXHLrr$i9n4yo#<ֿ1{g\vlvI'J~gtwZNHcɠ%i<ݻݴ =;P8JyM]zg/bt2`4m170cBn}?ޥ=0M#'D3OtwXS1f,}N{Ǣm\e3b-Q1og؏,;ή XϊmC4d>~ /ų?fej:N&رc&###۷o)SZg^^?g&22F-%Jj߱l<ޏI':Xp?xr X"i6ٜۀ>qŒh:va mOlh۾r =3qjHNr Y^5yP~5g m2?L 㶏WP_UGDDDDDDD%<ǎsz[nb)$;w.6m*,&L3Q +&`X`*^oJa` ͈VmbpR`Z7+lTXǭ"1Դ) pHfcQXʍkWrm.>6]Ca,"""""""RS_$|}}+K7ިPk֬a޼yՊJ,mI^[^/K͹[vډhܘEhB+y7l!Z"z}ZeJ:GV4*o<44􀬌7QF7>VU[>WT *yfr5`߽1\/׊YJ?Y-y(d:{wݏ`_⿃rԩƦi2mڴR:Oϒ;q}ݻL6K/83-ٻ O,`PsؓBzW0|z3|h㸛:ᚽ٧w_on=F}0vackF$Wj[ڻ&SW$1ϼ=$~>{Z2x\m76zIo0}+2e=NͥL~q'wxG߿"HT899ݹJOOOgj'55 6wJıykڣ'֝ TYwZTWys&K%5x'y\\olgz-~c(%a\~)݇bK{7/šv^yvOҥ<<۲f3v$OޓMo=grWK׳>"""""""";gSl]&ѹ9 fԩL:믿'V˗swb:YuĈw}USDD.[pimbj9Ky^ '^Nj֩5W\b[oT]xÆ T=,cǎUN]p8=^raӢEc`eԩ\HSYW>},6M'Vhpppp9թqTTT5 cfbٲe*ZDDDDDDDDDDwu*aC˖-ާ/W|+N%{]9Z󙳋m6Ǐ'//Fڪ ӄkIݲF-kٵ}KYnS6V-esjSם~N%۷oOÆ ر# glXdgTT111 &zzOz0!q*_zL`uڷzuǎߕ_uc{*8ˆ#4iEDDDDDDDvչ1@ӦMyxqqmڴM62f̘2Z DEEO ]N %f^f9ر{7AߗKW*o3*zuo`;`}ۢشQߟ +/;1<ؒp?O.$(V@NϺM[ꇹ|;ߗ௤ݸϿa7 A$ΙtkC K\UlXu%0!dz P+Ş IDAT[ḗRǢj<]uHmS׫W/x c˗s-{RW^K.g%&4iM]L.8h6iLޗuizwu8sn^f皬_HݸyKBؗwuADFEѨQ4>Ec6UD7 "i*rrM֮IW8b;0+'o/O s9y&kV'Ыsm:AL˖xxկT[y19gMR'gԭ[7f̘ĉ/Pꫯx'lsNf;b%'7}N?~4ÞGxx3\MQP7?˟E-v,*ŀXc} 4boG8ޢsGߦQVqP&ȹT-ZO?-Z4M^{5O^j 2f.R,ڙHm1 66b}˩.Yib&?4sU:~49ڳAW~p2۠zfqH 5fq5}DDDDDDDDDΕ:0\\ Cc,]777qqq!00 бcGzM֭k-Qa@=Oo9\~Ύ V7=D6%;J5`]4$,wځXlmIdHP4ܓIm\\\OۼY̦i2,OmON_ADDDDDDDD\G$O:|0SL X,v; ܺb@-X+HF.FwaaB "|{~~5^.lݑ+pX $N~^&11)k./Gٵs'vk֓l?qo(jm,X[&0)Yv"0޾7`QVBO%<-ĶτlAN^>+͊Ü/QR~_/ZADDDDDDDDcƙl޼'xZtu]vf0a%y9x/ -/OW֬&sY".V *^]tcu:.ݍVHT:gض_~b0,ІEEIfg 8!W_u wƵO?ew;0"=]Yv-/ë{u# 86c8!qUyl„=:?ለHu-Z@61%^<r~r"""""""""""R0@ c9A c004 $Gsf]oHRHZR8y Ahp ٤;Ӻ>a닧w8ʏWMTk=={^8'˫N}K~fփyk9rͽ9WL/Zt ͇vl11v$flbظZs/~ǿy`s^40>⣯ؓkm+Z/w_;]C0w3 kC޽58"""R2=:?WqH /|EwKV%QplL3͚^}H֍h"fڙ ,]ĮɮV5;=Ѽ-vغ4{aVXR>!~tЈOX }ڤuѹ$b?W]}36p引?2|Y=hRJڱns*kVu /wwjGd68L>+:D'SoLaZH\'?'@?.OPx#L`?+ B/rVv2bW2 GS_ڳn};rwpq9Nmqٚm`9%)DDDDDA l.lMI&I^AaUkb!uN"}]i} *8XXb Eq `u\^ 6W/K#߃fݻg3Yv=c[[mټ`R봈"Hض޾={vŃklY撰WzulGpdgZʨ,Z7,[|'~!~:\+u{bQ/.lߌQyvwty0<χD^f}v3j>yd1ɑx5$P+R3?H9Vs)`~3*zvH W9krhް!#ֵv\NO6 o@œ={RȷYؿw;qJ3a,""""rW=~ l0(:p2~c35aD5oǎpeݜ]Uh *ԟ͘|(JI`Gaz)f0,WPiy:-c1 4͓!04kҌ7#7۷$<7fJbup~Luoߚ ;*Mw'-8LJ\*޹z{~zG뿟5RiI WF-Yo90xxxs4h$}㰸y~~{L.O \|HIi0վaD7 eh"VoHn/q]Փ{wpXطo-5O'KX -aV NI^&[bZV>QZ^}Jw3wv?iFu?XEDDjylmVa0IZeg ƌŇצNҜי6OqW;Ԧ3ZbuϜ#9iwdǎ]\[v5jʆ{ؔ޴4ƨ,X/-ڲl&ٗƎٶecvBٸu{v;vqx׹:h|A,KVךGxTj$#-7rOFA깻0Jb=Mb.cɚ l\KZ>~[ݴm|qef515 _j6[<]ȩɩH}圯!GIݶm۶-uG g%)f̘j;AMn|IAUzEF?^EÍ\ }[#=9[ys4cXchYǃ~H\}SJ`Bۆ?DDh9g|8--ɓ'lٲӎ>[no`ܸq̞=zpW;1OΥxgGt{oz/2{ WfVƴkזz]լk}7 c]\c pb{ '-:RZO.{mWƹ-yu\mQrǣt9Y >mo&$dkC,_IaGl#C3qh;܀nx Ǟ#FHˎ`7ma“8RgؗJp^2QnjS WǞ~ 0L[C=Bo<߯dqi{+XKLH~0|8qSf/kٺ(ynycAWMl؛ON =Q}#q+!Ix^b1[CP\1Sp3vmr9>C_K=%i$>)͝surv/rI1"K{fp?D""""""""Rtx͚5=ӎnݚ:8-/pڒbժUx㍼ tܹz5gؤQ,M&{ ϓf`H-g=< yvaDX&a"b{Vz9GV&Y7>ng42 e ܊_̮Y>m_~^7@ۆVױ;*JzuB[\֭?#Aw3_6xq}3neP[Wp1<39Xb 7WyGp []WUƇcٻO/$;J쎺&tǖQ |?8w60IqoM۟H|= oY441>f ydTlisWxrp,x78|bUrϭXv2&UoqU""""""""R[l811{wn6e/_΢EJOOOg̘1[W+. n;1v/q2a#?/KAWgс;>Iƿ+WqY S~3WH#|>@i#?<%ABOޠ˜,Q ?ѿąXzz}!@P@f=3~& *9:|[f`u;b?/VW8wJ'Cp\ek?bGn(_G6c3s~a-'d4 cnڅUҠ0F^Sxk^ً??1{;G~Cc:u"{ ̘[OiǸFӴuwf-(-kV|L*f|?DDDDDDDD|cرNő۷q)SJ3//￟]vU/~c'I17Fpy>Fx:u͠$ׅ[w_1~wy+9-3^6|0ɛ IWv@]jlu*}#|>q('rsp;{MpuO"\M,9)]{&Y\!E%_\Ny-l/mRZͤdg~GA/ WngkK$m3;`{#s1XM!lm ^,(bUۮDk~9Qf;y;-܂R26w\6mTfYYYL03f8|yl8nt}͋-7vߐpC= Lfx0]؁v0?˘> k0xa?{dt0;ߙw J6V8pa`fxy)i%6#Z>}18TMTo)l "ΚM)өYUjU WCDDDDDDDDξ:7_%))9___x7*T5k7o^bK~pc |$M[(>ElX.lل'yҴkG{رG;o|ˇy0 ӆl!Q Zt 8ii 糓XkkH5% ;ɫBqkV4we}\v-;D4nLW4ޕMfٽp}.s{o\$7n߫n_dg="i'?MgV!43rb=5ǽpGyk2z voJbw-!iGs;,+}Nދ "DƬ$MMLvni/m74SL7EEQ;PQ̽?Pf@Tܼydp3\^MzV\LXlRg)OVSIXNJ*579j"ucz/|q#~Rqɥ/z%^4=˯Mt~yx*a>TOϛgyBß'9H_^y{ѓ$Fus;uc_35yUAĎJPz#)e*0= K7cZܩFHu^q~W{76[_ğ\HB;odSsyynCǘ%Wx_0FzO|pgddٮyVسgOB!B!Bu]*a|_馛U?… <]e !B!B!.靪-r_ҳg׎9“O>i.KʅB!B!RYSwwv?rfbMx=B! ~u[ m"BѲ.58""RU;Sk}k׮v'B!mV< k7-g 7_37鉳]߂.&j)s^c u#b9jz6Oc| yvOIn[Y%SB!:]ao#GW^޽3g}}bbbbB!|FSu\^6;VYy=h:jk)~7f$1}Q~{-mbaܧyCL;d ?/dPjnV|$ ֕ٮ*O!CaG}1^|b#Vf\ 2Ec| \~ѡ#+KPdB6)TBIy={ ֻXv^ގz Oǀ0wfjz]BJ0UWGXT Sv(F=}{;a `:2op_uG23cQd#?k!7_w uz'oe혪+&uP<4kr6+1adBc`7y0UH&&j:27magMz1d` ~!Qhf͜ǂ "#ۡ۞Ǐ;ssBYgң78q.E۪Oi'BK0NLL$44cIIIL4 hH6]om6뉈 ..܂B4 *ı_lؼcuZr8q8:Km+̙3Y CWr>رևvN)Rѱir]&dBƙjku{$rs0 Ҥc{C9a2մxCz;qٸqdOʢ3=a>.\6a#seɄq ʬg=[.\]y5'20\o!-%'cq%?CC"ʳڬ*O!GJt:nOznn.gnW={/J;3|%ߝuټp/Uy}=;.J]9_Ȥc6m} !]T=|}pk&4 |=cW[Y&w^}Ci B JgGr3Gr tn=1::Sk2A;!DE1lh:eVJ[PӍ`E{bO>04e ;KkSY8\(u0|`ab %}j6&5= %<,gw/kkOtחPܽZT-aӧ!!ANK+͛sqjo;F-;A9x5)_ebw*bO}PyB!8?ԒW\q_|۷oo7xGGGjjjxGQU;vl,..M۝1;bN&^z/EwͨѰQgs(ISm'7/O+'#]C!D4M΄#nzc㖍  Pz];EO] ۺiQoZANpvq3g;RB`pwCDl`""2e`LXxQU«" aEލ} lqjiYDppxqN%((9UK ۦ~ZZ^{ !|r c>ө=@И,عs'&::={PTTfٞ_j4Xl%w*A~_px;DA%2,n=yVJ~O5dYFWG͠頜]a,^И:q j6{#y6`ݕI @nnoR,NhXZ`պL 󨩪 8Џ#2 jX6bXz ۶`7eMh*++aD4Vdpe:&J#:.ªٙk(ʼnQiC'SYghӫ[tzLe%8:<|]O|3GsviW]B!6 PI7n5zQ1a4:k fuzh>_Era/UmHٵٞ2P1dJZ{v=$Ze5Ne\z~!ͮssqn~^e*!wf7?Ԏvnv^qLNk>J/vVyvB!=z^kT¸ؽ{7sOez^x"""<\is'~(*k7vhFG8@ cP]keK9g҇1l(5 @Dhљ=YWoyXr=4$<,[֜ 2P*p<ݖ~>0b`m8qp䗕ٷ=Qprfպ,}5ppbѲU!=5Ovٻ?anďIYeCf>;M3:5' w"˙FbU5|<HK X\j"*2R "w[_~ 7]{ ^A GZ*T?vf?88_mtVwf{ձct<ܥksؒCƉZ-ʲƄq[c*Y돌2*kL;u8S:u췦{2 730.Y@!|i!{vo{K!Dצi ZƆ~NQPb5Qhq*'N؝0n0֮`ǎBÒ* :G*K qFQ4"C ǯ6a:ªkVMLq~bTUGI~}|t~Ӻ8zzW)w'8)o-Aӷ^B!Ώ.!mzz:@ cs 7pVrss?_u\}GhFeLky{NlL4}STNFPtQv^Oٌ%'P͵G8kN_eɲstV*OX 9{{ MqPUJOYg`EhgܿNa԰[bg&磟6Fأۣ}x>Ən(ĪWkk?DyIZV{Ǚ}M|zu 5BFCb1cl7!ϝᣚ`4RWe 4\[ yqrt ,g۫$[GB\/vg3g 6Q1xYFr QPbchmjS=9ۨ(C@oR`iqFZr_ v̦,|=4vnaeF58Ծʻ>ϵD ΘHiMoVym׺xyB!0tgϞ|駍KS@h nׅr=0atsHzs}Yס)3CZQOL7VRZ?|qHGU |3< JڗZ)4 Eј4~4ށaswu֔X'6@S MF[9mӹTӱsTR'Oæpi' |0SF HAiמqvXyM;ˆ5Wg GהŌw߶Bq2q6$atXaʤG@4Œ[n=D{:/ovA14&c7 ({\YȾ} oT 't:u6]KU,"رcK3^xNjEӡ0ö$&yf~Fr9^LRƕ Yp Yn5{ƃΈhHZbGaF&l:,TW ^~YGIZs$0$zJ?q0+2W`HPX$f`7}>\7Ŗ;8S쪳_wvR8Wwpi䭪)iWYO:Z/Ǝ7g=1=|]:<|(]^Jʪ(޿ @,VǎԬ eAA6ztoX/aCPs> M[(-)cwW~B!B!v$+++)((੧B?=ϷO^5իWO?5$0y׃QZ [u0;QQ`T~y} `!9ͥzZ\]>f`lPܜؐ5qrtwl-d KM\Ѡ o_V ZCu |e^V5rX F+37R[SI/L?[d?SB)FADz5kk=yTԲ.k#e'0f3uʑcU)gsu6põXIn~p0 a5& !B!Bأlqź i/p89GJژȎ9or5S;LEk.qtFl[-7/۲rF.r$B!B!WPsɿM@%3Xq߱ fSzrCT wl#GI !B!K48p0a@Tx(>k uG˩:QB1;!B!BɒB!YB!B!DKYBwB!B!BѕHX!B!B! c!B!B!'IX!B!B!4G!B!B!6i/TB!$B!B%)B!Ycc^>ءtv5>+B!D;HX!Bjz6mLGe[֒Z~B!h6B!BMt| ?`WRɢ>ct|#{^(KˏSπz&2t)w8}d'X|96KB!袺LXUU,X… ٽ{7555fL&hŅnݺqeq嗣uvN~qgLDɑ7 Y׹:3Ʈ%9S;aHW3B!-Y R#Ĥcy&!mH*FWȍK~ຫobt`!"IFE#+w ~^u܀̯k(ٹ))xpq9$[ػu RHގ=|x1\~9h]TD^1yabM9,ZUJ$w, ?~l5|m߹sKdߖ-ECA<a'ٹ9yVp?7Ÿc>^kpy+)BN%rL?{5kPRRBEEE%L&f{1>T\ul;lShٺW69tB]Z6|FK&1i U䩊ΈX>cvVt]h!B3>(NpB| TPIu!0Зa#8VQzx_cI^ưZ)+=DMƮIMKL?[㵦:m ߇> btZ*^lޜ !թ A7WCa\ KIA~OLjװ2 )8'؏!q] -!k1}DMJ#1?G"P!6tϟgƌ$$$hgJUUfǏe^u-[2eB2?r/*Nh]M]Kv<I`rƥ)NAwHw_Czl!BN#{Q]mg\rEGiY9-"Vd5^gTUjjh)eESWgT^N#00J*P,&?T"(ٟ^U\\.pLyU ExORò] B!~Dxoc^N_M5 IDAT__bbbpvv桇bɒ%%Ryk̺?gs5NJrܹz7~(CS N=DΆb<Z'ߝ+9/k3xtd@טn/BSK`Z]A \0ѭ{T@ʸ4\B]$ 5i]YBvV A:_2,4M*8pSVh _o !1]"GVPP.X*,,lu:hGbPF_u//={!`Z@eWpE0xU*4]B-%~0R1|-z2m#f,9=MH0i|kq!V+_>A)L|ʾVe?Ft͛hE藷w~>zգ %9y(?ĻG9}uY)$$0Ft^QOL$ip_s7| O`ϱvmt},~n&HRp&gd81#BqIA6o&[c|=QuTW|<8ՍNS\UQR@b!~{d|h"e>NxO~M8#q}H7"GRKB!ĥK0jX˫sj7T'BmP?w#^?(ԑ ?>=}ʝ >hŋyl)B\=__ #}Y}ߋ|n-)60,9r1A:[$gQoAԱ嵻3,uz=ԽwǟzS#tQp C(g\'W7?BjZmoaI2<ʵe\;Ŕl+{zZR>b?F Lf| n|~ n'F=ڽ."^VV)Z~ñfKlg?5$<(v%s4fB!.E{"K&qax4٪2bl&''#ѡ1V#5/rmffz=~{LLk%k7ml"דIc3q@3g.xDst</19KfԜp[ڭ .J #Oe?0! -{?gOx?S֢ Dwsp"Z!]WH_J=~QxHfjҷ?(FOj,`ٳ|S#[Il| ?L_ymmݓG)iiru_;jj8ѤYj4Z}سu-^MܲՔ_;6,e]]"wGYo{QP^-$i@Vneu wys`Ws #:ٞuNcF!7$^181^:,0exNQIۓ=[_ N>^oO9Q*{걇Xzbk)xKܮ7= 85=zC+TMߛֻiQg3l](M̿Zi%?1OC=dM/u_۪DDGFU9٢9A91#B!B!D{tvyD7>$Y Pc+;\s$A^}<}>2%dor =w )Yvx7"t%>2n),MtO$n N*x N,{,:i$>J;m!Mc 9Nߓu9Գ7Q]L|pKc؋Yv7?g˘B!B!dq'r!B^@xpt2Q So=#.C!Lc&0Ny>wG}FrYür'cX WOo8JYٲޞ(;uqK'4Y6=.Ί0-[g?`bw߿ƻ;q#鴹O_ \ñͬv.v0幇_B;c k+ n;G&cľ&}fØ-HB!B!D{4GSmw>|A5kwucV2y|Lϯ?ew+oweLHC!F+䗹)(F#g숸ipH$Ѹo2 Rz%]DAbW0kI9a(gBgGɲg_pu>]㌄;ၵl^5 !ۼ@Pp ˾ǹom׫;& ?yBҏ|;x4$gd7iJS[^}?`^IWqSDv:m>.E>+,-bØI;)BKUB/r$B!B!gyK[;5X mP 4!!͛7_.v{6ӯw@L&Bߊ7w#B!Bѕ8_?Mmj%?ʮ?'W$<}N?]WpOf!B!B!mI_Urp|NW2=q'd1[!B!B!$aBb3B!B!RaB!B!B!~s$a,B!B!" c/Xuuu8:B!B!B]$a@YYT]N!B!B!ʺD¸gϞlذy V4ܐ=Vޒeغ)Sh yOB!B! q /0o< 9z(z嫪JEEGy/0vs._44wuVc.[ܙa[m+̙3Y[רdnEk4`TwJy{xWg7!B.O;Ʀ178Ŏٺ>[]¹s<8sߜHr1N.vW^y%K.e<ތ v_WYcަ[;WN\w!Z~:_AiQ2a^ i\&aAwߜl!njO,0#M]"a˂ XhvBMMM^͍ݻ3~x&NN߆-[q+>pEDvJ9S'M1h8D!B՛5r6of߮TWc)x+n=h>?8_='4}<{+ pSǟ23|dVSk+h*z7WBCŹay?v -ï[Gxx|2}4fy =v˙;< >{6Ep_E9 Ӟy1]!N'qgȊxe6/_pZJטpj݈x%wθ͔d(uN_N[enzjjvݷrV93w.hΥ;V}e_;OnPm#1ߵ/[Bt14$'MĤIl͛7wVXg7, Hfr6d3&8Yƚdpe:&J{D*ZIa~5U1zD>Ah|EvPYY^@?$ް%̵TWĨ!`%V**ѡǨk׳xz@c1DGVv2,T#ftPt^޳|Fb)iced搗ښJ}IOK%8[c؎4Uy4Ƥ "a`Zr,Pm z!XkM9k7i <]4a>?S/ tո|5?jkͨ'pw?x?L6Ǘԑ~կq֔ڙ-,vk;!B\L0;dj/\w_MW0LDz/e8fJYE ɟ5e"|݈@+f0%ؑ?//h^;z@ԧ ÷=Mt~xLkX)fg@#m9_rHfo,ժݬ1[Z70;6}oeaܧyRo;dx߮D욻jWL૸wZZR|:OCyOpj)6;۩dn}h& nGYO+~<@ʹ}G'FvKjF6Jko_6ƅ2 KEν8c"6.kK~e8zxuE3cGĪjx۞ͨQ8{j][p{'׍#3lsrixtÕf# ",:>ۊk߁?ff5YnN;ÒhnDxxٳU~%8j3:HM7m|7t5xA1:8S?`n͟4 -[SS K]ײ9lY!C 7OfX0Bܔ&\6YCns|yO׶ƙxz<>Vx=)۱!QX\}p9H:; BBCW^,e9?X,'wۼ -8r1Ye3LNCFc Jq#4~|/Mx}> UWRӭOV=O?kj% x x UG]ʡ mf^BYgң78aG3X.ř{~?X]!>_Ozcz;qۓqxnRIa3as]0,k[ 'ag&oX@t _:mvXwofRr 1D\ȊVwJJmiN5 lb9x;/la1扏s(0c߄۸|uBwI& hl͡_^`t$&*lȀ#[.χ37kj5ldʨ4c{0v8yUJ%05_/""fFGE9dgceS:\vES7 &q q9٧)=|n5  dkvl;:zSFL J&>10/-%+{=c.?m-Fn*{:n*۷mb°,'4,M[OS{2ilڰуIHԬSm /\wtOX5'XgѽϭmuV~GZn/{bB!aع#q`u*CۓOT,9yVp?7Ÿc>^kjrz痺0sWmd)j n '_*ʩ4;^JwVf6jUSLb樀_o#>??&GC;:B<χW)(L^,{3:'Mo8IioJh=Nكk:ѳQ1%Z%Oq ŷh]!A +r84wTRG\>Zly{Lԯs{{|֎1AeT|]5m7?|wCn(6 !D$KIi%0O|rV3`pל'Pf/^dY:FUU-me0@XD4E[vRZ(Vѽ{'s߫''Ḽ=mijޯֺj zogEKq+-1A5:!~3)?8 6 !DroٵkK,aɒ%ڵS #==cǒuGsZ`5{]Q4v6MPIG옻k K)bV=L pݷT5:Q _ 3k>f·i 02MC ]s)F:h$d}h[c_ELK笹Oz\S`,UuPR߁\TGު=m'sjƋ9#9?mPJE:t:nB[H$qy)))S䱾>m/Ěx7Sdez*/pqr*-MfNZ[PbA~^Kܾ4muCqUlLLtVx;5- ZD(斨*ZXz\e.`bFri}GϪҗ=i}*ss]i>kVCASq(`iaMzFzǢUt.hS[]ƭX깎B!n-KK)iRq W\lPQPjL(.>R/F{o`dc됌V\q?$ lG!r&Zk<#C욼g߷jp͛UXn 'I!I IDATjU;}˞dv040yfޣsF.$=G Uwe4Vt.yTg[;m=~7:5w;?'g|'з^y+jm ?b2rY[/bV wV(SIMUvm|?MgyVOWԒ !pO$,<˚JXM&M{]H] rAV7i]5fy/6/!4 WL?CV68|4M㸸QbPL9(J㪎Fw bcёUm5 B\*%M-"fmhM->8.7-]:ݪ,6kG^ŠWا,47iXJ0Û"=Nv;_[΂}fO?/n+DWG@Pۀ.\.Dg[uV.s^o[B!.?F<OX4L~wp"lQEAAsP%ɮ0vVZ︵~B+. &l9$47n ZR6&f{kՠB?5wcl|Ӥܪg&Zbx}̑x_(4tc1[ p;]9T0.>Xpe1iZ1*%%+TVst5*M-i`s٨XPWd>5Mn䚺X\^n|Ea q\Լ]c׸1a=/g*x9`ޥմu:;g͚l+:oߊH_:uy=vsZ VxkU۵LaK> spwqbp~82ëƭjSXYط/9;ЙhqrJ]ֱ-:vǓuVtnW!a @l޶`l]+> C˦ s}Qffzcne}uڮ> W7O:_g5] zsq2jB!ݣ(Fڵl3,"DAUʸPnOWFbԘxDQQY~sQbTБ_>ONh,m]ص2>fCI ;6-ƢI|0z6qEWHJA^Y0 XeeL,=Mk7f|Q;U7A+s#I$kCdRar:lt x;YN1 |hdz6kE-;W*gǼ(~dijC^mY?"Jzpfrxf4GJA2ns %?':hÑ~`[8%$9kSc]kj.v^R}j=u>j;Wo7੮`qNƅkj.CU8mݫкgTa4wenw;#ח|[vyD݄,=r$9h5 6zЦWSR3ٳ'hTNl 7@T@U,]w,:vAհjVO,]B /OcitA\bYf7' _y31+X`4+_gT[G͏Cgжx|Xo)Pr}s> [`p0.QSf=Meϳgٰ8ĥ'~^UWm^py3:c}Wl̘4W@McOYgr((>OoXYEiN>{]VJixt }]ƭsEg6ìl8LV~tyl4â<*j9gmqOSyK,ao\\\^wp| Y0,qpay Z !DXYZ r]J$ =q;%?$B!BT. c.B!B!B@0'iװ!w; %חB!B!T !B!B!$a,B!B!$B!B!B0B!B!BQƇmٹN!B!B!.1aܡu;B!nB!B%KR!B!B!$a,B!B!$B!B!Ba,B!c)yqX[[퐄B?DB!BqLvV6/^d޼IMMȑ# 6WWWllDnBq33 ʕ+Yz5'N ??b nIt:,-- gϞFVgo oCSu_"n2ͯbS'8>x4HsB!PU-[wNK@HԻPvvLO6mPdPǀ(,,'sz^dYe.B܄{Qȋ/Ȅ ؾ};dgg߲d1@AA٤}v&Lq0xБnŠfO/W~]c Ⱥ(lŒtB!SUOSsnR׽gjju߷NT7T/c:Rvtz\W.#Yg̑$\]!ID+شi...3tX[[th$77b2228pӧOgƍX~Fˬ?w~KDv|'G 1O=Uou_2}Æ3dMY?: g i ż/X[A׾˨ S!'ƏBӫ9ȹt.Y7*/Mq}27lW c53&DYԷE!B!ZYOfʱ[x0%J\uU\\={Aʂ+{nt7n<ayIČkQahr GX2y̢T!uO$׮] kF=ny[[[‚7xu-a, ⛗cQG8Qq5-!9F_ 3%5o0@ca=~n2=/dg$Aj)ʪykR. b08fe>y9]X1E_hBĐcAV̜>7lY\#*t ⑩H}XEblY/ ^Agvg{]!B#{ٲ#?`wo?,NIHIN𾃃WZEVVm)Lz}y*+B!n=0>rwJ_Gq6e<3g~,i+%l0E9AXW+~bO;}^ʆCk_&Gq'Y(,wwjD,>ǵz#)+>aV7|:#?" mz6W^ %|x84BwƿPV/5^O-HI5fo14¦44m֬tZw;C5JoJαyT]_8u_gPcVt_}̌_ws&eT0^"vΧ|x+G9ƽʓ\%i}_,̑,`tD}|99V8̜_"!Bq{Xpwwsׯ@IފKsS4 "yFtFH r1ՙ`GtX9yp 7 +s3ڷjOHDj5)w ܼ+VؾkWS?›owUR>k* cOr1 (x>av6y$p斟o9j2Cjv1&>D>8燵dC>~R&Dpsxc1^(;nßeX;O\ n,ϩt8zXݾB{"a[ƬjK (,},Į¹NxVQ` DKѥ d\TAL_|7>5/50F ˚4[x`$.`e m_)K(c{`,YA2ƔW>DYxkb`9Ď/|}ݲ1>7i_+\W[g쇼d}?3+_= zT:/ǎh<nqpLdlc;ixWUQ'Y֫,<JX!S1WƊRwTp|?&>Ǝݛqsr5 Pغ Ҧ}8zgӖufq"vz7'Bol2ƍ+gaiI}O߾,Z 4>pr(&֜\Gb6Z֜<1zi腿g*ڏv (?38EDS~Ei4;:VI7{OưXZ!:D7if Esޝ>.uIg" &UX6qfd> [;ˊ΀!3{V3xeSb~EP*ͦCi 7cq~Fk¼]qgS鋧2Y}ϯfe؆G+?u+k0WNdzT6JH;[{@֏͖DNd" JVيԥNOβB!Ľ Ӟf;]Dp(u&;+V؀(%x{U9us+%aP añwr sss{+V,gŊv=~qcayKdMh5D$g] sa9baO[8A]mhmE:7pƃP'!J )9UυCwU % fɲB!D5$a\O^`0/8Ȝ7Xo]܃=GѴc <1}2:sʥ(6z /&?tIa{Ilj !Bm:\\\*䇫],?VѢQQ1*ݸvhh+gΥOVݲ FP833\Yt:Vʛ4rje%|hz<˫φ`z(SS:WeFPjT8x$c`B!D))'ړF iaq\v;?E fA渫g9}ra},Y%xb/^ ƵyWQS/_-//@ 1@?ܬ]U$ag՞}Rٳ;ڄp=.b0 -hB2Mej  Yi1WZʙ;1vB!⦨e΋BZ5o+NN][ֆŔ /^P?/7>AMڲ7 :Í}/# :8c>i/ ʽ_|=QxtЂ'@{ꦿG9e װn4 ǿA^6o9M13 ڏB 2(ryzૻ̉F<||㍳ !5 `}b'O~̏/#m'D  6,E$CBMs֑;۵r ׁ7~.KgcS4s#DB׭43g.̊I?柗 G-zhPgwi5 c><;k;sֱ/+V٭v!A<ߗyg]~N_K.+ģ<5u&YW_~h;3̗>.䜣;}īO9S֜W(f|B!RS3KNVv1c]5B՘bX+6f`b Τbb,Mqfdv$/_ rc5OҥKٴq#%8ZBвe+ Zh!6 {C YN-+#i;~@ʆy,<O*]غ-ƢI|0z6qEWHJ'OukStv4.+z 7.F}P/eߖvJ1/JeYĐW[bF9EKvﱐ 'v(+u RJBjI>MY7ٟiZmUی9Yμ6yzj$[Eb|iEzqjE׉CU\t g"cwKZfhLp"H`TpXꕙLsoV|䂍;!ѵW$OzΦ/aNDQud ،&ciL_ ffc4ͷ9]$͉;/,圷XN!y~%kEfT/K_b@WZ]ia4l,C܎?0<8C!^(Zל[DJv]9bUνl#]ڵb ls}.~;/@Ϟ$/ՀQR ^g{ĚLF@@`5ʤï5䨖b`E'l-‹z{ 2w7l;V`RLFS\UoMA66o`<: l|hYbK9mXybxC:8"qϡDk{(`$}7:/W1{M[F䨏xv6}ÿ僕 ]+@ !kU]U9vCfw$Hw_!vڲs#Br$Bp鲞e˾^=rmX|3iii|= !B\# c!B!( /|0E4x{Ѧc_ {h[B IDAT \M !B 0B!B2b$u3C4k<==~y;# !=0ׯ6l`ӦML8۩S'W}*̉YH%VǶݜJSlٺb()ֆ`Z툊9yz=+K|=j m*K `<3ұ4Ҁ;j9RK/[B"[U_Qב$.cQpqviDc7 F#,W0Vprp,7R*,g|zى Xx1/27ꋹt)}Q-;Inkc!B!j*&&ߞj=f'lSz#hL,u#Uw= whB^x;[5?O<Ub3l[27,JL iJ<™*B!1&K߾}k7"" *zK(+0_ИY~"r2qv@V-.]Xm {C W1Mwqݫ FC͙w Uu|/rrs`ř.çNo:t (}#EXYѴIA2TXi'k7TOHf[@3gZvVo}Dtx3ׅ_.-o";w:O' :ߑ}g;:k_pM/B! fecZb^x,/ѴM}șߖ[rB+~KSM`[ m{>n39sh9BqGɿ-FGbR"}P5dq(V9VQU9q<驧i}jjޘv-1j⹯aPb?tsbKys:%og»uA_ldݬe9 fq<-ޥ3c ^쁩'ϳux~ei"Kΰ2?оtj['wⓎxd0>Pg1BUU%%˽g4ݿ:bڵj l*hO`B!4>aт9<:~ONX3(@܂Y3S1p3 *5fr9|2fvx+lR:PSKc$3y6#U3W,؜ĩl:GL㭮N(5Ţp|h G/bbEy[Ue9"OaE,4՝H9K zhT׷}Tƞ bz Q$}`Lgߢ]ljLV9AMѠgϴwAMA(Mcs3 *e7<j]?B!-w6Nt֋qL}D:Lu ]+zK:ɟ-U%24MAQ65-D_Gָ"P@Bw@@H#w}Ysp/}__?,Y06ibPݜз#8Gsr cJ+(TG-oVZwiiڽnUW+%d:W$?KҴi׽oW36'I$>~|{. c!BQG:[[,(P InѕAϼ.۬hiYđ%40"\1-:̊p&cjGcc99ƆcmARK,/,e8=px96]Q* +c58yS2(o N#`ʩ`OBLi?p4[%Ab3fWQԆnYHΏyuc)Bܸe^4C8 H Dm};@?!DE? r~e%닻_ cc^LghLJ׬Qeժv<: 20bu1vѥF|3/+;]\HCRT\Lgfw)3k}kۗ'>=O~5fzҦc u:oM\8PnzB!DŨ#=k[y<fh>v u(m0ae/~+y<Ĕ Y7+fjڐ+5jcifv,Նf4j`\t=wH :מahc47 ǃ,}#iy_i͛/|.yn,KM&p ^ s,YwI3`9p%)c%mi$`]b5IUBq3Yn֭㷴mooo޽;͚5ױ ՚Pgz}1ZmdDKxh0lgu4lKi5Xgۛoϝ#)1ʄww7@gɆi. S[z}l-Иx"U||@EUUEo8yWlʲʶ5,}cjrZ5 {h NuJ+7fY^]Y%T*B!FJ_(ZB{䧈S0:Ω|.LJϯb(Q0ȧ$(' ]i<RjEۥmc[N=AU$5u\piܺFtǯkqghA&7(> ZD84(g Q'D mx\kUEpg嬡!i`m G0_#/zҢ'jo5ߩ!׻'Ʒ3I\^JJ 111;y(3QE(ekږ̙8:UY OV-YYWƕP\Gkb[XPh05aNiKX9T=hML#(V^VV-}TsKTL-%01QXXXc.`bFri)ɧh*B!Zȑ|<fYW ߲@^G˧^,mPΨ(,ɦрĀ 7QPK,:;Z_.eKX1>|4Rp1sՃ9BMgݾ4ne ws#I'NQLd=+t?Q뭅FV]DZ#wㇹzv݇\ܮ%8w,%#ٹy?pCeB;U賤v܊&fgfm<0s ܘ4 ]5۷#)-ZDqvBFڅYYhhܤ9[wbԘ郾؀(&6%])QulFPp()*J\\9p/\<)F [+D6kͦКZ^}]nZ - ט.4@\vFFv!JlLϜAoPٻ ]Z!B(VbA/qߚԥxpZ|u+8qֈGgnU|up(j#Ϫg{T.&C7gm~ t”ݾs3SB=]mAνj5X[Ybg%;g͚l+:o J cZbӰ%n981?}nв)\6F_T[Y~ޜ>~#K(xyعyЅB!MRjo)XAxuSLXKtՒL#VWx:ʟfE.zz&3c1Ox#~._"L.bS)6M r-QŲcK~;#%:#f*e3R'raQ)In$mzO#H0'yVaݖ}}x}1|]UN?1xucЏރ>_i͠ެ2&/'Bܸ@WСuB!ݷe>GHBLۧꂆd^zi,{j&~fX\y ߎ}^1;NKi6|r!SLx>gЧy7#sf5 D"eU6+~ܐHjS'|W}ϳgdc.>C xe:M m?YzX]UOxx0+ `LdEݑș|:k}Ѳ(Z$1c3M-8Ƒzu odc{d] 3 jXndyjz&4էcxSueYa,B*XYZ r]JX!B,?e˖Sv=v#BI">f\K/)-BM%a,KR!B!]6dP zB!cB!B!BIB!B3i̿_y49x營/Ζ jN/roL(!6nB15g +~O}̀DD=Chk_GǨ`FTD8}Ce 8f x蹐iҝnMul}寧|IFB!Eˉٙc=]l4ˣ 1c6l` 1pҀ1"ʮ&vӴV51zj^WzԼXrZփ^xzy`TÍLm~)W_\~WʹRКYa@˶t0ֻB!~>>sze˪lի;vloڧݟӘ8w#o.[@𽓘4"S20{MᴡMkZ)-r}K+- uۄNbjSG8^T#gU e D3kuƨqUCvnҴ&=FNX'Z<w`&axѨSW(:|P+ !N"af^xz}h4H`` K]~ c ffc{ϟEi>SX2}F'eηNdʾ`4_olȣy[#ǓI豾tQS fԌ7\^9W%YCƾ@U͆^bd3 IDATQiyʀ˲ĸ卯[oZW: _qOvs>ϟi$ l\%[Πf.]!BF`Р,Y^|_0p~>C  cG|?c-ӻ~oPW'5z7r ZB!Q<@֭o٘c4h??Z݁C3{\#&::W3swGE?u&`X:t{.x搫Bpp04w".N,?m͛Rr/6| М] 2w;:gB + u<Ͽnk ~xâ?-|,9}uCt}D$zaUDm;{ؓ˅K{ϟ~i݉;|]eq*k$hCdtgj=/?EF@TTz|/.Pebc: _};Ֆ}f֞Svt !BQ1;SPP}CCKNΝ(Fᬦq:;mUΟVuv&;wdٺsaO<4&fuλ|0~ &ө!3WylI&qR wĜʋZ4I^2>@|:nPPḺ ([H;FNj ҷ.f;é߲mU}37/~ʱ߾ba/Ƶ%^47 9.sd۵sHG^ȤEov$=[`Zt\wztk=Co.ӧ 8UX6x TUHoK8h2![| |&~=Pas=N~yBⵑ^ Hmr3c=GDPp'|}M]ěS咼ِoo|ԓ]xD`B! dԨxu)<672SYX:s1粊Q, cohirD&L?$.׀#hZiq83v/%Zo<Vmt?6u' 웴asw:R 1"aOR˴ {)|O7Ad?V|<E1Sh6 軾!;a{Xh.b~2P>WDeF㬡)u(<?3$7? i N}}]*FჇ&~M}CApDOr et4-!5ctmU{~5ʻBDv8nA}(,oS嚑%j;y.NbV!B/ѫwoJJKx1r$kx^<~ff^D یl惇r}\6'ZGԶݟ{?[ɽ5l2Wmp&j+DO{ZGYzcݮnUcHŹ/,[kQW!0n 6O3 ;[oPU1wUѴs/#xQ3?dp+5>ma3l;qWdc(_ u |ĺL}}OĤhȻ1=6wc/}p5kOMFQbbcx0&?OU}ޜ/-5X:ۢp\zD5u.`ߺ'!GV30kB!q?~`BG6 (yp?^׷M;D⦞&U/'@zNfe,^EiF;̑<|/Zbϙ3ٗgT0̖3 w JO8aPwL}hbA ^_U_Y= \CRXל Ym=ZkQ&;NUZ"cެ9~Z`'.@At(̙LLxe '곊B! PPB!ĭ&MAz,oC._`k9U87ަ9^YGsbY3"'Y7o-9!xjKƏP2X_ ~"6`g :/|-D6B2#]/%UrLۂQ!7w6cvnߎ'Ŭ>^X4'|2;fif@pJge b=R٭Ј9^qFcZԂ+eF 瑹/XZ1/7ᾉ]u_瘨>]aΘ䞡г'3SN)2 iB!B4zט&AmB" EG/sJ6ZkUJX|ř"4NvO1CedȂs8Uއ10HS0u2Ju85f/0tN;ѹ+oТE?q1=v[16f1|C<1}k<;Zűtx.Jo`-~vӘÇQgO$ݚT$ 7O,WXAxx8"kEԳ3n*3ͳӋ0qtΈE裼D<#_3&&B!B!j|]%1m#nI_JLL%qBiDHB!B4&VCBO~ "X!B!B! !B!B!DB!B!B!I !B!B!DB!B!B!F0633䖍Y\\ Nec !B!B!Dc(z[6fXַlL!B!B!hE¸Yfڵ떍Y1VPP-S륪3qݡ!B!BF0ի}K.%%% .hFrrrpdҥL2={pY2/ܦn<ع7ӷ;!BTUƄ]?B!v0p@֯_O\\'Oe2pz50oB],ҳmo}[ydr:"7qi eeXHtΨ(m&QZRFo/:oSUv'?y/Y47%$)1^3 ,Ym oSkR۶dgaQpvrUX C#u~nnQͱwt8Hѓ$$l4L55K,SUXtN&pOm{Je7qL67-V!Bڨћs;Cڎ`4廥?eCxn1ƛl?ǽ֓X8f;!B#4Faڴi\իWs)++aeeVE@޽۷/͍-+4h"l٭+: K3)%Lssv]LwJ df yxԣ`Zu!ڴj#g]d˖8Mе JIص;a(B_sգE%\pv^n%Ci[G?:6o3HLnڸB!P^ͻp3ɦsAZ-0nUˆ͜=T#Z^ [cT;[[,7u*<^MሯxÍX酗||p ;_܅y~/EkLgǷә.i31c殮ԝ !$"a Iӿ:ۆ]s{bb .\PG^8yu;+ xFU?vD]~͑p|1J>șco~Yuxl#/4 g F@ʁd rpwu8yvFN=Dn^.9;-ܽܖW8j5-l [6P\XV-ӶCP*.ymcM6@ehx7 gDtLB|KN 7gGtݧ @LajoΧsQPb?ϙMFu #/`Ԡ8zr4{R$/' '{[:wlo`HB!u u!<3#Y9y?KWfWqWY]l;ʦxNik|w#*LdќuS]POۯg֓9OƜ2/~w鑗y4N_$g,Bc4񿅽 Ɣ}E{S*ٹ؟ֶϨM=ŴTѾv"[1*ڱ{i,b7n#t톹=ⷳ%<21T3sROɚ()5e3]==u`Tqr(>1YpY6mwg|C+ hڢ| [+Dؼ7)G7?ȏ˖=ðsF:d1' pZjATUXVVz6ұMI++WqPMܗB!TU8w(yh0hGvuBU%yv 2ѩM+*0O$@1vlKs3ZuЙf=cF#~Eז~_fa";j;65EWEi| ^-k8D":!hIξCkX;fh 'CeDaWw>36 ee-vF5sFdFF0dZNpSs5w K~I'rE;ǎ1yYS*~#goX!Bzht #GvZ֮]ˑ#Gnh^^^t҅={Ѡ}@5EJJJj^h $4$ {ٰy-M4ڹWgf㥪X'ˋgΰ_b cs)nn%,SlltY-Иryk8@EUUEWe}k4ʥ -=UѦe=c`TLU+aސ+*CMv&]I{4f_f笮p>{IEi4ޘQBbgmMo{<Yj4yP]똷WP ';SBi˵v=*qwnLY9(e{_>777NDk4RVÅWs}kippr@)&Z )?2k\=p䐝[=9L}/}*25iޗ#638ۇ~CS[pnHbC.ғ))r}KwI !7IH$qeNb,XcE'RO$\Ӷ'NΚIKvvqu#5W,`41IqL isH X9Լ2hL' WgܨѪtbnE&&]s|g{;0qd*S^p ]+s9ةt wUg>,-\̸X} pqHuB!( *&Z#zuę4&&tnIVW`8Y"*8m1*fwLE--ή`4{yVXo V315R~M01QPz?Ekkre=^F牶ϭ;2BE*4 vvUM=ĉLށ/V}n,LJ83OSf0Ϟd Y42fYlݶ /7~ IDATwLt\f֭zbnkO~ *w6( tԑ?;F2]ngeEH6mǨMI\BBZR%@jʖ4 )eE@y^IݏRX[hqv:BxD[mFkfyyѻSDw֠smں[;rI(дI qtގ+9E(ծ/\TvI$QixB!EQT|=]{ص/Жm)ST&WIҞZDȶtVO6$ij0(lb\OfVfuZ1VdgٓWN: m Y'~i OVJhChfϞ)(L_entv_]iݝN⥟Wh.i#]z4'b))BQ$)<\l:d87oցjʼnaS>VCnA)0)n.<sTl8G.DQ͕CWe̢2Ξ=E?;vTl[:y/[ųc~1ji@)zvŒ]ɉmنΌAu=\lv';oeۮ$Z z+KlmSڹҟW`nfR-a еc4:S vST# ޭ~Uf0sg8_\Hʱo.?Q<|Hl]='@Lt+J3~%ŅXtanUbGR8d Z+ÇBCFN!gDA!':FEApppe9- NcǮxj0@yLgIseg`a\履Uqpʌ*9y8BAYvAci?'vNvp%3$5c]cS i=[ K;W:FI);aC1g,x6Ex.Y=g=cX5k<_myO䁵 ({!ڑliRsѭ9|B&.xԑt q$Eniߤ !&"a\ aCjwպ]QX[k& 4VZ>H۴ZxnuaGlRkbE.mGTy\/=3BdXHq9U(65Q> &Z0ZcLQ!5ƒ} ~J& bcSc^1P6!Bs~sjٞN]zPѠ`Shou3)*>؝ّVkE~@֑l;GCXۮW=U177'H'VD`Ӄ];`ixy݈v!uW2;0יℙsw#֚ѡZTbZ3{(-܎-{BX-Tc^jeeӢ9C5n3nh, >۵ַ`LG ,#1f߬*BkC"hD'{!7@MOMUӶa !۸m75o !թy}5϶j]!bP\ɯ{!P$+e !B!AR{$BŔB!B!n/VO2g|JB!_ c!B!hMu7B!Ŀ$B!B!B0B!B!Bq$B!B!Bu,zq[B!B!B 㘶*!B rW!B!))B!B!B0B!B!Bq$B!B!B0B!B!Bq$B!B!B0Ƴ񋼵,cM >as//}Я_?4zl~Ї dO/aZus) |d%wXQEx!-fR{}  g0~<(zQ1[ė\im2o "0ֱ8n ¬PW_\~Bb!:b {ZƠ{ע= !B!B!SHFynCZZ[laݺu|u'[b`~~6JKH\]O[+HtJ gY>;~JgY+'Sl%=CsP )3jq.O*:GjFI a#^o Ĭy& FԱ0۷7H-Ҧ _qO^+ ffcϴGW˅d6.g-g{OW)B!B!h ˗3cǎ%,, {{{LMMH^^dddČ3ذa˗/guЇ;LhӁ)=<.=erB:=svrKH^0M־de׹9*nE\(Y#Юqt~罃G($}]1rjLʈ&qp\bK=xu썇9u.B,qmօ_|aWW(;?ZED` ж3݋h9~ 9S*C84C/-5yMũ3WD9aT)7^ ~G|h))(?Cm$$|_>Oaڂ{ߙɘéyt,wzyjUB!B!B4ps͚5 03+zA}}Xw6oḏJ;^xl{_[GZ7MƺWv)CAzwM)*-…|!5cr?~}֗.(;m5b$mkZǽBFM!B!BZ5 㒒n٘cU]/o mwbnJ x#7,,QO8i_>%Q>#S60D6Nԇ lRFN` w[izkue{( Z5&?OU}ޜ敏^- g̥*߆S[$*{{&fT !B!BKJDPwx,_uϡaW) h#7W ގ^ )XCuSc PEQP2ʮo-Uz!} `D RN/Jo砆skN,۶ۤsǩ+ې1o?-h}0Kc玓jEAt(̙LLxe 'ùB!B!Eߑ9QCoDc^yZH~!XXX?V &v )&sr)TAw]dݼm!/u?^CqXb}5PĒ{1 Hx~'ųo1cvnߎ'Ŭ>^X4'|2;fif@p*ge b=SWlbyhD|/8C#1-Cjbsyr,}Tp.r/x|sLT0gLrPٓ+fٗ7Sxe^ =V\!B!B!I fB#^epj ((sK J]L\9\T}2ld9*EgCg}hA%U0i*N~23ލԂkouleh̬pp |0^J̈́YL/>k7z{py3uq, 8қ]G˱4f!g`I&es)|:VfcP'HZL>ʌEo"L>-]E裼D<#_3&Z!B!B! UMU6@bb-v+BL 2,6G"B!1~+^B!B!B!YN!B!B!%0B!B!BHX!B!B!%0B!B!BHX!B!B!%0B!B!B4%%%lbt:-:U7n`7CׅB!B!EXuƬY¡Gμ䙳d^Ptz4Wm4ˣ nj3ٳgè*p&^ޖWʎAU(,ȿqm4 ߷my-]9=uEgaIzF:<(;3$OVazQѠ(*mR!B!BF08p ׯ'..ɓ'߲qccc8p`a\f''(mwcgnJF|ϙMFu #/`Ԡ8z0šm f{"Quڅ 'OӧY8q[8,LC@ F@ʁd rpwu8y}_$l@qaV:ZlN]@ѠvFN=D~^. F|c5%a'{wSTwqVTw?s^.u}KbK,OlI$OhIL4cQX 4{yva]zW`993LjRcG"-+w1ݳy يUb%j"8o9$gkDޑl޳֞|u:D`lX_ʻ֭[#vOl6"""ڵ+SLaǵz#5)G|g,%k/X vM{ vםD&#k`,&⢣1-v֭_ǘL kK>M`Ӷdž1yx!h>ZW0a\Qq,ZYy]wEVdRM c,Z1iIdu ̤ܳhMD]}ޅ2vhGmh2훙cܞ23i>%W۳)s0'"""""""rf|M׮]O&yq8BAy̟1V]{v' 0,BP:mVuO^7fە4M )Khv.*"lc򸱄 oy>V+!o,+p"5Y, ج0ضk/+V_ea } 7tRlfkSé393N|if&wo?ݻOY 0>;qdvΣpj6;6zoC`lIYyY۝`٩?8M+UUgb\,XTUgw8PZ2jhSSIINvfSQa@N\~o yy}I'`Yt1]zpB hRQVBlƭ;[CDT,P]c. e`> -?= pYc~>aK `#9-GBL8Ii'ĸX.#"Ť#OJ;۪w1[z{k--t`t\H2ᙗΉ =L#". `p3\t$akgPXDDDDDDDDDDZ EDDDDDDDDD"pw8eZa,""""""""""ciXDDDDDDDDDD""""""""""ҨՏ_sSYg cH#""""""""""(0F EDDDDDDDDDP`,""""""""""lgC ynp1᧤{놀M`fM`NI""""""""""_w#0XIC&|eCpp#! ǀ18P[ qظKp@U1~Lwɯ6,Q6;]1C&v%x>~.}l[pgҨ2184J6/K_}Wc9y%OĜE?'|PR?ץv|z[.)5?ЌDCCr7 yI=ḿ|u9\j_|f]&*%KLsvA7b:`Kָ"ڗ{Ǻ_PVac[+ݡ@YbMՅhiq?]oRSp҈8ϽB`!O65 ¿ b~#, O3Q]SkCYI֊31cۣT%uo& TK|m"""""""""!>zgaiӷqjoZGĞ}`91nvk1# [v&\KN5o\c?3\HnWXxKkdKX:_-XOj^/gjЎ|[@ӷ o_g֩scQռg%~O6)b % KJ að+Im!$=:BDDDDDDDDD:_n72)-%k%.Yo\j}[H‹`F66ص~gxJpowv-"""""""""L Ǟ+p߄k%uPc8{'\b[6˿ ZسXL]\8/kG8}:0`Nv """""""""1iKXwUanR<O[0g7fكɍG_{%mĕQoR_ŇI=d`9;U6{!p, =qbwt#"""""""""8 |B.!h63xK 1mMNĬ?$?@-5?g}]{;otC"@9e3=W˻f0_DDDDDDD:n5""""""""""w""""""""""H EDDDDDDDDDc:H`l}viӰ;1EDDDDDDDDD:k0OۘFt\uէmLCƁp =mc:5ٲJ{L 9;c< 9&ӈ,o<ɝ\ z""""""""!t7kpߺPu9ϋ驅PX1"1.,1ػ#;^)ÂlvOa_b~mTuVΚSZ;Q9l2i)mEmԟѪ:Ou0شco=Ig'3:?C{1t!N۸̟Qlx+#S53Mw>yG<ت#UEN?ހ@ YDDDDDDDDۣCƄT&\c/5\ݱEՎ ?%C:k Dw-0Omk1XŧׯiaM])]L8EDDDDDDDDt1~ͦolo|92>:{ҕӈ8o"=aR00s.C-@X&X1mL#%## GDӍKX %%Cm8JN$97p(?OȈ@e Q,Vw"|?(][C$ ^ V~HOO'{GVF0*ZE..$_mj/#&.Iypߦ}zq1_!#^HKt˽W).i}nb"`MldRr3+)&8y>7Rρ}y)t9=u8ׅaki3,8vCyR"ރ&T4Q{6cg"zI^alQ*[;dDq9mdDN(vGT 8ĝѕ] KqB5y&*AO!lp{Z5J;#k$~"ť&&>%^RJ3!gubGŪ2&mĕ<LJV\z}#x!vg}~@*m TOޡrU 'lT #Mӹۇ\Мj_̦ $ ętijmcnJ=IuBXXDDDDDDDDΌe`|&W3/~؛؂[Zl_IX^,랤lmA JѤ q)$vNFv\(2фb}~ zuƹhUDvwa,%;>,  #/G64WFqo9ʆї1 _tc5! 1W.O⦍>w+*Jzӥش bKllɹ)1X a]q1D#51Ǫށo)vp'kj7R_GX^,aQ^+PA}[s,{ ñYJ6a"?~jsn*ǮֈeYSɏ*S;򙋈_L /ꕻ79+m=$*a9AԕSaFl-c`Gt 6'w1M$Մ0G;+hRLI ("x,%P䆌4q{]f%PoQ9>݉$,i7دjtm|e9"&#=t/mm)*SHVBq &|z*`<= EDDDDDDDp-;p-);X S{78Ne%{W6?jۃLÂLho\kfM %RZִ'p:OPٽv0 4vaciSIF^@\+]f|L̍qosx=b-$. c9c:D`UMYSvaӮ;5q;ԔRf#=) !#[R8TSmB\ut217 !t)%KI ! d$bKTGeqi b;^KO8.4؏N)1XJ,4m{ⱖT4k{TV榵zPk~OJQUw""""""""rt8}̦W'rD:M߅$SVeѓˉFA|F*,匥w|1F=]Ϟ-#s15xl1D;Kmhorr}/!)eUVr7vl;*h=YqUŔۛx[¼سy.#TQg#Ҋi.}^F *c$vt榵z+]O6i*}c_SlӡCln _BK\i-&"(#,UoO?%~2[H2~Uwq'WDrvȫ ̣op\"g*QyB>zy^rWfO'rDo)0*<΍~r'ۼH>~Nkv@37ǮRټl]ݙEOv6EDDDDDDDDOK ?]㡋Ns9e1g<# &vht r. _MzQ,YBq~-NDDDDDDDDD$)0ʹCC0.SUH3]t EDDDDDDDDDP`,""""""""""GᄐkCDDDDDDDDDDΰVOW"""r,\L """"""_SڒBDDDDDDDDDD""""""""""H EDDDDDDDDDc7Yɪ:ӵtp3]@K k׮eڵQ[[K(tbqb!22C>}Zb|9#'rӤSw`5G|4a^k(Sۻi`0K/Dee%ݎjb9ѡP`0罹>+Wr5hlV}t"*1&r-3!u{4{*;ϔo㆑op߲w{k b?\.wc'?qFܛ^˓&,`1Sx:{ l·_}'ES{4~ 7ƿ!.޻ {<06?03yQ2:wκvo-u4n{-"""""""".0^n TWW3ж`&>>kמ@`ۙ5mCG02j'{C c'mK9TSMJy{#,J?×?em9'OB8;n.9wqx{Q1ɓ3`RVR )YOrN=*v+ϰ(*,w!1ӧ \"xOwdс&}j|=GO;-Ys~Ϟweao]~yoNR|'?~v~啼jih̘5'޹?naOQI9tmFh3O̸|ٷ{+10xƫy1WSH41{Ml93ۆO{ {p#V[1z̽zv.nMSˎkmu."""""""mᒠRl6PrJib)))9BC>jwWH̠ /cֻ{r :PU9U6+ޒ"͗̆,p4t{YSXB YW?ŜWcY/wOeG/;S(?,ۿUX]]E27lhk7+{9+Cy<k矽KZӲɰ 7ݻ?{?\|ĕ3wn&Нqn_%{dY!Uu?$Y^{'job'~ǓFqݓv8?27}-}@ɠ]9.9Y:g>UܼᥗJdz?lO$^JBY1/,bmIluV5O-qﯾ;8|}T_7n'$( հ,ǔ1 }R: A"2VȂ% cxײr~WbD aT0:b%ƈg]?ebͫ7syL>:-n,Ԇ^I%~~'?/Cnx͌dLwF~[\Q~kpq j~%aXVB1Μ̙3q96޹za i+z#/];O/pϳ9`bSEG]>O~-s~o7 ]@ fBޝ_ǀ-jr4+j̽_sy"#n7 ~=C0d+nJJ|^=<}x?|ZEuxjYD-Y[* ača0+>^L M'gŷ^#~wx%sfkrI#͟r{?qMkCmg+\8 ų^ p]}-|G[}O~:pq(b0X,'tڕzwdiS@raAoL)d"ks$\_~Ă!,ISFYy4[FIA|R!frq w{u+#[29`(HGo%S~H: .#g+g5SDCmMxi IDATmCk`Idoo{oɽcZ'Õƀi7p3y{~^X'_ᷣx֛ÂvƆՊ!0b3m$3,e(:-0l׶ĬϿ><5sO]S|٬G~I\{7 ^rG?ÒI'Wb]AYE&{ݮwYDDDDDDDD:\` ax<8mشi7nd8p^ƧzÁŕϴ9fhj>Z27UuUKYD'Z,s]xz;i;~8776윸C{ "/}3m\} HIIs΄5lvBNp8X}WbVPU%ɬ,fL݈`~?=SUg*d|ۧ|Tcv2^j.޼2.A\>#wgIx{Y.MR]Ư{>9+./]o(Mu;Fnˢwavch',F q\}3?\0 [39D6y5/p[EF r2a"Ւ>ʽsOeFq֮!\rA&W^TwrN($5ksE=7ᕏ٣y3wx?26m,k8n8ICR>ŻoFj g=Ƈ;8aḙ?aʟƫ/el^*fk6^\S Pvk.222HKK4MV+IIItԉ}vM>16bSӈzn :K#$z{\>$Kd9<Ǭ7aNq-!G4ɹ}P(@օˢxGW0N!<蝼"w.!T-/s>#GoelW2X3*.zDHr+uh+ gޥަ ]k5!ֈd؝O׸?[k!*ngKt쫨HvqI[ә߱᚛=/0W\E˿L;6 5=>—3xO' lpC=s&ǭ]ᕙk0<# ~'CгYR7k ڣHHb}i_kvh)t`t\w} 0`>[ҳgON'>_=|>mFnnQV+_|kʻ. _gEDNV0_DDDDDDD:n5Z0޳g;wp[]%xؾ};yyylfEԳojq_v<9Ya|{u0 , ٰ@MM imuQQQB 8'64B{a|u0>gqӠ4MGyy9 a&`0anIMM=Է֞gt NgNaa߿СC2d^cijg۩jvG>~rz' ңG***jr?금4ᶤ8rノ܋0 6=eőBPXDDDDDDDDDen1p ap_ZZ;vپ};-Pv\Z],"""""""""҆PSSsTMtt4ׯgDGG}TZbbbK&|p _]y\\훾DDDDDDDDD:>/%%~rss;v,cǎ%77߬bf{&l޲ !xǼ&dBϩ():vOE]{םJfp{XCtR\\Lzzzv@@ urqhO#۴i+Q_ŗ\:\Gأ;z=̥x3_}8b{˘ \q4:uc|k\udw;j܅kX}~pFǷXSю#WšLOaO^;Xh O^7'$ z& E\BJ 82M=Cvl,4ClVb蜕ɠAKlS17""""""""ルiҷo_.]Jdd$ꧢݻwl+ʙL%B?zBJ )K7}ILN]%d|p32sr8`bb0 <r29kx~&0.z1 R]L0gX88 .9ozTr{/[XrcE.:u|ݼ׹Iԙ`^~M"L&Oy kfjj)-1F4Z]S]Ujat5`--`ӆkIKIdOi (J"]L=z%eLwri~!|իwאѣHmqB&<9'5>O/aB> {Z=5˘``rId)X2MD}{?b,Vkm[<>dgA};w.7v3lnޚ;O&C57y\uHcݎaG5UVo>222ranrۇdرX,`L<81.A&&5%N]^C}ذic=U;oʡnwI18;`)Y]{BM]k 8o8[kn3o.W]?nb_ iŽݻHx!V^ÿg3IN6mJzl''2IࣅعqMǢ˙7u7QT瓏0ax>9ݨq{jƣ皊ƴY~cR31G/^ΆU7j4 ^%Ϟ͵_FljfsdE9cG"&!]{ٵ@SP @VdRM c,Z1iIduuͳ^g&vQF1`(,,d…,Xkz0`FjbQOB9 v6ΛG}ueC'[qIĤދ'$`Uٿ5$F 3 >{?C0aϦka޼X#={dp0)!lůKII'3+Ν]oRG۽'i)Lx]a )96ڹb]L4՛vOx'Tz_06}!++'ˎ9_MCl\F !dee1<h"#HLL$11{cxOVvgRSS2tums{%F`sQ^^G>#feعs >?ݽ.X: ㄄ ;qS> ,LBB! ^਽ à?{.ޟ߹GOK!!5uגٵ/lۺ#&{8D97LMFL9 1H蒙ȳ9j_92Ƙ17g6=Sѷ #+ fJ+* 5:n ֒ӫ'ye,AIHN S1i@×QX_`! MIE[SF}PM#%= U-;Y|)حV~?nkGKZᲐم[е`v=%PGn;uHݻrJ @AW 8fGn9qpcJQQÆ ;:8) qajHHowJky qc 6;:j{f2L2=$C!!tDDE|`Crk6Pȵ"w$w6s~$`IVJrsL&+39뼪|ȡ0*4ao)qiVD#+%""""""""5KےYfvRRR0 CAVVxzzҴiӋR]͂+0uiz`!%e=߸!faa@axADՉ%%u=͊nn6 Wfˮؼ[ ;63g ]pB#+犟Gq$n + S۠Qf_/z.j\` AoBB ]JL&խxx'-#v$g( me+jPWl6 +0v?:{!ԪOhު=^'g4h-n,^ܬ f@?UyvaLxhҨ.gdef` o`6Wz<7"f[@3wzwl̢k&,8! ?tOb :kٿqtuut /^@?j҉_fNmM[YI)X,f^=UgEFDW:g`מ+zPv-">u+ ~^H]""""""""ryhuBm/q9r) $ u]\$Ve|l5W"""""""5Z } ׸=EDDDDDDDDDz(0a,:B+EDDDDDDDDDP`,""""""""""eXDDDDDDDDDDTzӻE+^:DDDDDDDDDDUwnxDo-)DDDDDDDDDDP`,""""""""""eXDDDDDDDDDD(0ܪ\.ӧO_~aΝQTT(vV+ԫW~1`fe""""""""""PCcȑ#Y`6tšCXt)g_WhWaym}yÃi*__kܫsiX`jpρ"''"IIIa„ ̟?iӦ1x*Oy׃m& g忹iD=>{1k<5.0[= K\ Z[ ƛSB~I?v +> yw{$M/` \>4ucF]E} !}*Sm'Bo2r6up cfpLY8:’w7ٛM.7iftJx?rqosK 8`YL v]zkjD`o0fs7HÆ <̜9cfγ|׷Bj&O50s;w^kp5 x"cw*f&N6l?qnsb7a41XNhSnjdRv_k 3WI;_12N0y!)1ox?XB(f'i.=G=|/'^Orfd\\.-{Vo'ٟ^6Q#-[ТEK6ɱnzg{8Y:Lpb|ݕ瞸p3`4ץy<Zwk"iGwkøj̓ kQ6{pUcHo^[Зd9^ɛvw>N_%o^ز'18s?G;Ó'o7H**̒Y47v&n=%tnėHyjhsl@mo6fdu O-7論tw#7~ׂw?Ǘyl8MבջŬ?_-}̮"^XkLy%hҝy:03姘piG2q 5ʚczס-cyzx"XlMBbb~øwo_:~#㰸1izϠL?.^P,}̔'.e\g&i<7eb?ץyjD'B*seWteF@VVdkİq#>˳_R4Zx/(^HQg`p3ѷL/3mʚrطl*7xӾ١+'ÁPɶ]Ue '\"SR9OBm dR{ض nZ#䔣GsNbZŚeg$݅{V/,{x>9˫sz^q IDAT8W]U͟UԈƅіfsrc/z73aLSwW1r8k5?%0~?zab82sX:u0U9Za!}YQ}_w%@!&jQǤ`X:&-7wѴGvti! ѲqNѤ$xycZMWhƪ945Afְt>lF~HEɕe/gUut?""""""""e`sdSG~yU~.,důAWF8Ew^5sfw1j{4^;?+e[9iy;v$\_&~}i8`wxR򧥿HrwEr,FÝ "~֊TG8E~73>y>#b*>+fvӰi8 ~<3MG5|jуXЎvQ!tԐs٩uIlvCHۦ hӔhߪ=""""""""RVmL~35CƷİYῘCnSu7ɟ4' mSa=pkH{!a4]kʮlK7S%=1-^ї&17iP?{fDf֖m!pc,?Mzo^}g Z&>9hue8Ҳ3}]*°b, kƔ_?dj ݸ͠؜؞p\ج8ڽ.o}t?Ō4xM>5js6$o'M'O-G8sҿ3U1Zb,"""""""8ӛ|%?Z/-6bfvUcPd'}6)21{Q;;c&}:3f`ǎR\\L^^E ł~ӇϫEDDDDDDDDD*jD` 8p`m+=|Zj """"""""""VPWر3g2sLvqQk׮ՋċڷFfH\^ZZSLaʔ) EDDDDDDDDDP#k撏Y><֞""""""""""XDDDDDDDDDDԈZ+"""""""""RXDDDDDDDDDD""""""""""RF EDDDDDDDDDcH""""""""""[eX{jVi`ܹm⥪CDDD.+""""""@Q`,""""""""""c)XDDDDDDDDDDpNr\L>_~;wGQQNo۱ZxzzR^=ǀ0@ ].#GdNIVVbҥ̞=_]5$06m , 88￟xZ89u\PTTDzz:)))L03m4\e)oqSzs޿^77ǧ`/fff'~au8aw@` `[xs ^h/>G0R~^!_x L$M/` \>4ucF]E} !}*Sm'Bo2r6up cfp#,yw<}yr3<>rEdʝxiMQj^몘G(WIox㌎J0nƨ>3' ?c‡߳xaauB \jD`o0fs7HÆ <̜9cfγ|׷Bj&O50s;w^kp5 x"cw*fŮ&N6l?qnsb7a41XNhSnjdRv_k 3WI;_12N0y!)1ox?XB(f'i.=G=|/'^Of磓hJ߱-?;:fK GQXrio| agqW$Wmq~"mWX%?ֈaF}O=6gΥ'i=0_P%6,y L- o- ޴Xv8ÁPɶ]Ue '\"SR9OBm dR{ض nZ#䔣P~ʼnL o;()mg͠?ox*,p"Q `Kq}"""""""""խFƅɩfsrc/z7>gڌ`L'ULaA9ٛ^m-s=}71X/䳙U>HYGѮEG!:=c":R9ͼ1sou+iGpUefѴ Cqwtq+PnG~G}}dL'Vz-~r}3Ar{yCzzfn{ϤMMU'""""""""RԈR9v`)\#?ʽ_/s[s'""""""""RԈ=/lǨOF]̧t.[mm5wՎoHyG8E~73>y>#b.|/\#Miش=/@wn /?.Y#8t ?ǐI$4TP<^{''cf7]xup%/8GOCQkCv.xzybNۛP)J yo0f1S.:Jbp}"""""""""5VmL~35CƷİYῘCnSu7ɟ4' mSa=pkH{!a4]kaʮlK7S%=1-^ї&17iP?{fDf֖m!pc,?Mzo^}g Z&jJ\'үM!5\+vl]MaٺxY?֩Ks }W^H5P`|^\M>͟ ީ|F\;| Ү~ l~泗>@F>f juḡ8>F^66/s(T+Y];ydFt{քZϳ/wM#0r{]E`]<sKRD||<ɗdWDDϴhZZVs%""""""Rxyگ@^Gnϝ@FމH""""""""""(02 EDDDDDDDDD!fYPP%SDDDDDDDDD& 33yr,ooK6HMV#ƍt<9VF.Zs-eKꥻ;ÀIٷkku"""""""""rszͲex#..q8[rɡt6l իŸ4ܶ}"λ}8v,YkJ IlR+z_}jp2i ]U K\/R"""""""""4y`ƍw֭ ::ݛS0 o֦]vx8|.x| 'zjrV`p>pr`s~vUxR<U9?1/ګ Lf.}:8MMՈl6o0}tf̘;ͥ2AӧyN'#JJ\g`Ū9r;XVÀ{kT: U/5 tBQAkVH=Ɍd`vr~vsrp&-ZQl95NVNW-Ä3/f\7%SDDDDDDDD&1dUxrr*,^vw#J$ 8/~BڞD6hvVU7r| yxyӼYv A+$q,^թAmoKW!uZ kNGJKWcFNdbۣ3b9{wn#7'.jGFPAS7nؑh2:儅|4,Ԋ0F|5a)AL<`SV& 0UZaL\hiǼ<1 6 g}K3M[ n^ޜc8cDDDDf GQ-uԈ KKKcʔ)L2va7* 4;(SΘ<<1 ˅&ps_A8Z`?psg߾=n7r{ *]ndzދ1 (,pb-[RITXRRӳKpbldu߳g'o'2Q?#ԫ| 6{Fѹ47p629 趽"""""/$0}T5"0k.㋵'vOvKqqB(6,]l pl0R6o#8EX[>#F..bŎsf#kxKfuZDjCײSYr~?k#\Y޳˾˳ """""R99BN:Hz^(F7u: ʾenWʾ yiZ\loĞ߷(ŏkWĿ8I?~ÜKdEԛ$5nz'"""""8AЍ"ʭD1H_?;qukBDEŜg2~[?~q뢔 6jy= &:Q/fy+6w>* P"\kR>|NJg" {}+>zZ+T9[3iҷ,ޘFf:4vֆj--yoŹ)pĵЛ/XDDDDDDZL> z7mqBug?2蹷tkarc\ߩЖKOXN}DŽL4қFX\8#s>>BrwQ s&6[8P8y&?QKƾL&'浉wQŢc"""""1ػf)3˧+ĶjML89a5'3^\?|6oHygラm{bP@A_Ǘnèkpӻx^b$g/iidSbrED{$mMxn&7; L>W(DKzdo[-(CXɎKسn5ɻ]d䏥B]O]CʮCdxFѤekT݇#_~aWDoJ d8+5KIݟAv^OL;ws(#1mh4%\=6oM@?A\Pc.߯vd^UW=; kI '7ė=:VGfG8t#A<8k8Jîcb"РWs?C[bUw`6(3'~}!oM~5-/mh͏oZj]Y{~|`V!n$ooK0^͇EyOXUe}Wuߘ~=ȟkNٿa-k=gER±sDhgs=ϥg-@ Ǔ2ov$r`sQrCN v-sE0eiW`\gԢOX'G7'gTH'[OL$1gy2Dgxdc LV4__ P e[lR7ky0y~ tl8CےIAa^4 83լ ]< wIwq,OK#;mB=0aܐ`8md%$S (,e^5J'#+Hʉ Ef}{O^K^5sp޵j膑5/:JQo2RÇO2mL8궢} 7aEݺuO3Z'u:1l"eġڮ^V0jb#1:͛1 kQdݤG9l6*׃ _8O{ EWE;{~fDZ,\A egA i۸l%h9r}lږGxGoÁs ,d|3 ^7!b"=GTh@05G1B|:-AoΣ><&d7$ B {0mSJRj颣:SjGh.ZgGVn-eXBX[(@!a!I L u,s<}s_={h{igPPKf҇@pq驮3A(:m喥#t{7Sr|ʆr}^?,/BiR7)O Ii1%>o3H01~qN>^}Y<)çQ~o,ܰ+B] uD(8#&,^z /Ϥe$Y%58Ѧw^5-Dr|k?dۜ -/rK cN#?=]#2w<򧗨+M2]zfS֝[H=72^1Z[7g /pWf.;2릦{I8!9IlXuiI&oHi4lЎްi %ni#~Qڒ)ik[Z8ղΟhk 76 IC959op  -G8 BI$'&f`c>Z 2%k86l>p rKOd,(aX?g_.!ML0ʎ,lyj&ˋY .$>7M^l5뮻G>H$I:!ahmiy_fz> ĈufB}. % 6RɐadBy̙7G^E?283A(Yw->c?Hl4~jf1vJi~st Xc1B֡gڍ,^`D)N"1gKqA:ƺ|;$.Y]%%t%JfVVzi{1&|gcdoX1ùG)['~|G;&[-==a')I$I/H&-:5j$Nim:BP4aT:~;HF2f>̞=GQ>rť9دWW Ȝ>)<,jRϥ)_̰Z^ʐB  J%2Ϯ嗷3lz9fNdFDE҈ wz[kSK6#'38+,"[UUujB:B4ǎ(gӺha!wmv0$Lnn5;e> >.=9wݖHQcٹfQʊ]ǟg;YSBLNўEʘJi@Jˆ?̋.= xF\~Vo?fN}^zkߴuog?e"F&?D=?d`])C +%I$09Gf%Kp\RݺMZx{&0 FC:%/Q[W&P[+X_Ƙi$0f:_g$C=G&J-< bvI3Lswʗ: kJoöy̝3x ̻&O=y/ ; ]89)Yd۶u5Okc 5t *2hQ9' N$-k<4aO ;:?[b32;p^[I(x~Ë, J(N!@q6mD24 +V(&_<,m\zrE_j8.8¸IH V&/? qCܣ_群2( 6|cZ(W|KMP\v9E=mQ;oZo.˼DIBX3O,N>xAWGUrvV(*&yB$h9>iw}/tƒ$IH8+gbk[X<)̘x*RT27jsC&bKx($1nTeu(Ǔ3i&W$r+,h!F!䥟xqY%\:ؐ? /#~Hɦx'>c(~<}.\^99):uXJ|4Q奌H/l\0B@$ [q\60~/lj"c@RP8a&W%bk+XTD,@r46L^_M0 Y6He*7RX^j!C*8+fxF{KOΡ31ӈ.(AR&cʹ1opnv؏ͷ}/@2[sE]6Z2뚉f S}517ݭ]$I$IG8hO+c'˭@s$I$I_ Q㫍;WǠ-):utwtW$I$It\D{hu{cRv.n3H$I$I$:Σsqg¸{K$I$IPgy aq=CK$I$Iҹ3ޮ¸0'A7}%I$I$IgΜkp+c]w8zH$I$I~m31 NV$I$Itnv:=8 ?IENDB`gnome-firmware-3.34.0/contrib/screenshot2.png000066400000000000000000003021601355437614600211450ustar00rootroot00000000000000PNG  IHDR2hȎPsBIT|dtEXtSoftwaregnome-screenshot> IDATxwtelfC{oҋ4AP X!*QQQbA(zdޑHfH)>I2;=O{@DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDdiWW97JpTXV\|36juasi~[gϞ߿ nrB#'UgӦMt:VK 4O EDDDDDDDDD`ݎged|zL`'9!?Z5kbi| EDDDDDDDDDiݖ iLl=11q=J"#/r - XR:ml9O zClllm ,( ( 0xgd9S\l5㼡`x ZEDDDDDDDDD 3ϵ;#}cӬlwZDFDp8(h in|]i1~=/00[VƒAjZ~>7YVN'11$Q =\{C 8W34\DDDDav@{V{qb} ,-i<_/pjjC`-)IF ?i_9;s#:S@h޴*!9u/#n1_ <I'ǜ,^ŋ1ox V >刈H.KEndK/?ᵌY#:db|*t n K⒠oՅ,] qކ'LǻwOȅf.>Ia y^lgްzg#|<>uS)ÃF(W%""""X>e.;`3M7{/G2w#W><;z4#[R}+ Y5̷Kv3gnl\Sas_DDDP0_+ 3ww>6;iwKqaѱ".(KrMΊxk$mu+CfشtL4-^ƌ_FFP58y EDDDJztY=,[vpnKQm[S.Oztه{8fZA=|ϋdogr*ܗQrX֠ D6nD9jӴ,M;u2' y.)^8m_P]f=cnc)|=A^3hL9l|~(f*]DDDubc6|73ٳ3Mh '{N Dc| O-iZ-zҢa0괠qIsԿ1Еzðl`8e[v8<Τ`bR8/֙~ j5NZj6eV'5eȿI鵤m[0&xnk2rdߏa+9dVs#|XB4IY'k\ OBf950.^b݀0,wu̟mjup$?۵fsXœu^lMKO&r׌==& q*bBYo,<0&mŷB 5;F(a)$`aI 5k2H uMU9 BN|]=DQ=lxՀZ F~3SMd+S96s[TºfG[&e<᳌Lf>'"""r m =Lt`9/-^Ρn VNDzdL;twSd`ߑ6A˚UnŅ=-۳04g*:q'^ z/.md'-߆6լwlb|6mж%\'-d8k2]dR69k:wEDDD|\A$,T :9Ѵwo#= -'mJ҈AfٝDա57qMnu%MeW;v2/~yc,S$ ǹc}_yʼy 1wEkws_f47Y3ܷ/8f;pyRiRʊ@L6.{-|Qً6bDSz"_=ybޗ߲띴9t_hBEuNdQ6G轢ryH1ھ;̓Ҙ/ly>ZxcWfƢdiF L2&V֬nyb9 ?.ԙMDDD!b \T}&KrI azilkw9_y ޼1B`#~/?2}4^)eg<ό*cñxH;r?ϐI/y^_̜-ǟ͵cІƭs`lBADDDDTv0UUKG i=؇~9[ O*oB:`H9kUf_]WUͰgyNr}ܟ{ұa;:<޵}&%p]ev2h72x=3Y}o[ 3@O <&=z 楗|`R {=l'B_EO4""""r1N[ޫF\-DDDD\Flخ f`nÆ,+}e&""""""""""R9iV9X_vX|e&WmMr"""""[Û״ҮCDDDD;>8`g]AT 2 ݿ,8C+P`,"""""""""RJ[YaC~(g -K2/݊LD(0)E>fYvfWal&V˿j4B/"""""""""R N+u8CBT-<Ӊ.2DDDDDDDDDDΈ %,, G-V0BCIKO/rDDDDDDDDDDΈaH:x0 LuN &J3zINN>H)>NDDDDDDDDDDXDDDDDDDDDD""""""""""K|޵nh 1_.'{e0q<_m7(ZbȻ`;捄?;7QnZbߜLdNXB_"_OTf}mÌS |_,'f8 %- C|/#GwQś"Y2=BZD콝8|+Sح{&E]$QcQ8:xzp{'?<.!0&#:[`:_k89 Fq^~ݲԗ bՔ?ǽ/\S>ųn%@&:.u&$;q-U 'AYmO/3~"%< f Hێ˻H'wtfRߙw"A}zq;le,AָpmM<ڢ.2Qd_-Yػ\IPYLy()h@*h\ Wq3vjՙM`zP#ӊDp10o3eҿI pdb̃띓pb ˆV jk\U)dMz8&?8ZvѲ;wY;nL=D #*^aލd:Kă-0Z~{8هffgbbb&#X[ؑ~Sn/u%]-aX fK8)tF`Nh\莭 +iS?bk P9 k=a^5j-9ƻjth=|`Dc[6/.J2"k`JsOH017}JddQyǞY֠H}w-5y߼WHŃ*!_ZHlbicb:*t郄 ~dnN1!cK 70HHƛcgX $kB´C1Zȅ ~Ǘfz=x#{Ώ 뗏c\~Õw {V8߈H_GX$ Oby=K6t+6\2!ii&w)r=9c2Y,<0:}@!{N{uݎWo\a0lFcv;OQfL7Z jJ/<'@=:8s901L{G2&k~ 3#xgkOv DP*ak{^:gVnٻ3Yogv> [׮k6ܷ}S5aȿ9nFH8~Ʒy% xV`sCU v02dO`8BmJ=lJSeUp~_+Mgr _ɽ/,?8M| "xN^ZH1=p{$3 D;^9.+ Hƛ?,5Z6=mਇK~q1iq}V@6p>)_'aOO6w"߿ƽp᧋oF -~!8$)`5"zp6l' N'?*k2VTs9Ẹ\8>pԼ hu)rcVG&|?p/ o2s;S\׌#B]A%km]721ߎ?p̻:9@`K$zKlY O2FqD=w\=/{>0ljKf/X#@xAe_wsg,zoXvP FvӸ/+"@0 ʬD@涼LNʞa jzoa9y_Ӌ#87 g(X{\y Pe WֲѐojF ΅Qvn|[iq$l"6wjOP<¿s2^)]vv`fcz;a*X ݊k;b'M|{0t̳^Fq'AE,U)7{ԤEFX%MƗ5""""""""r;'WbXKDnț b+o#csJWuqymocI2>kg8]6/)9dO祷7y>܋f\N/Kn^d\?-ZF$\F:Uew,^LbQq)ѭ1uI!)՝d9]!Gs+!gq旯vۓXݸ9iݸKt9nWd@%V5GĭLl]@>*Oru B =,k%SlNH!({*zDDDDDDDDDD si_Y#4A#d>ٳ'>h$FhqMDjYcK覟9q ֝H[f®Z[1 'S6:փ{'qTw`[+I''_H΋3@C'hэic~q&a7Ѣd~@V -vv@|FPkg$:sj$6@-+_HΏO{0nB ys8{J5azݤ%v0Ȁcltn 8C h 7-JJEH* .N=""""""""""9ocƥ9'ʁG j w=@ۏݸTj,$lFJ-V,"""""""""R.teJ=/vPl|5SdAa泆+Ă݀Q[صCүGDDDDDDDDD=ΫG}Mcz /ӓMCNC.'/w4e?P,ᠫÑ MzDDDDDDDDD ܋ +m[o&3v>;dM:5TjKx{C.GC_;r*8plEʛ{pU:ZRۻ䧮yX mQuD\3弧Ԟ3 ά1zYȩ/^Pw̵g)I NE%>ȸ""""""vg[!ξ }ed-)DDDD3a^4?%""""";[RH2M{p k˗\׽31U #@TT$ΐtVfL@vZ7f!#c =n }Q/-}ssk >ZWO'"c ävٱw_#$lBTA\JO](G8wu725xqʳ#c9}^'~fvFy0ab̞HK?Rb];hQ~`زWf*Fӱm;"c/_ɞHOn3֮k7cu6֭\BfF*a:iFM6&LL*4lKVeӆ5x\DѮu+baieƂE$FFF:Fѱm;"*~  ݯGEٿe%1u.:o~ſ2f<8BР;v3sƓ4p!Ki#{j5i.>;'wNXd;7.͸k?`x1uN"\~Y/^ғ UILv`ޟWk\KNSJ ,[ÆUh״>]֛J8u*I{01س\ޫݺ"e_aOw?+e- e #a|0OgƼ19Ѧi/&ШP="""""S`,""""IH=[sYشu3c @V6$l\F-T6qqt؅CpG)W&TTgxX_111TR2嶰q2:6kDM@NqˎbŊTZ 9ii_ybJ*vDF=-aáÇ}m!bu㭸mGDDDD$/,L(ظi#qU1ؽs#=[oX8^^e`reUU[:5kҠaCb]{0%|.W|}|k9V3o0Xz\xZ.GDDDD.X N>[uybiP)DǬt6I#5j5i`abBc;6,Y6k>=s߬Yo.-PY(rċԔ$s=~21%0>9r[RGDDDD.\"""rhEi "UfH6c4Y@pVTIe$0LUZ L_ekѰIk񷲱Q`޿qsC` M2eJ:1Z!gT<+V-FES^;=I6n,V=""""rRK )A&u7cњEtp34ؠNݦ_Ltxtj׮_i$F2e - !qr7^#@F-j=8nގ;yM:awsՕ0~hba-S~H&P䆶5aPܰk65j79ʼnU'XDDDDJIzYf1+].OHKXⰳrz2"8FԮ_`rlz2ҒY FӱK6EcXj-b"gOU:CL'^w2 fvZ7f!xrngi=l vA|L#GDDDD0ݻg/\9iIѢI+8k gd\3g_d2|@DDDDDDDDDDP`,"""""""""">|Çv"gN _ɸzDDDDDDDDD8cͣ>c=.r]9[3CmJI`&|2Nm uDDDDDDDD;oTҥKYd HKK;c^ZjxW0SKhݼ)M6yvt}IǤĈA}] hG޷sYK[sϷC,;[m>l%|xzkI3I5Oy,3c#G NhѪz6 %{!vkCݹOY_dY9(o/M>Ai`cx{φТ,pPZ """"""""=[iPgĈٳsh"O?MvJṻp{1Xniⷤuxa}*bɩ{tЮe &>E;p_fdkYk #<>?'ǻdJoAxm4In$u|pz@;C|~O5U|>r 9PTf-yh73}S\c˸! ,̐;y0~z*63@DDDDDDDDDΎ&06Mŋ3af͚@Fx뭷xXv- K.r-j 0J=sal?Ry+`#9-i֬)!ӹk| *r scpQ_| W{{~ksa$ }Cy6i{ד=sd-ȄGXޑMUN.qn1\$۫1o#߾"P~˟1O، ivzd_C׾ͅA6# 7Weə-xF~BᥫsX4{5w}^ }5>iЍ;a+OILEZK,w&^ ۛY?RD cgnbd["ܤ+74:߽?/eWf0őfbb総 ؛]V>wA1ż,pqvv6)))$''m6-[ƢEؽ{7 4c\8q"~!3gd̙TR֭[ӢE jԨAtt4QQQ: ~?nyo%s26UeVglkT~)sר+Ai-m,9Z706ivr&?cCy^c ([DDDDDDDD5}|7} M6\q\ve8~?ƍn[o~_eŊL4I&f|bfqJ7ga,]ZF7K_9"(K|??_YNW\umWxZ,E6Na.6,F+^dlhF/c\=՜$2}xfcYo?<Cf~΢!t ƃ$S2yaR6d{aLʅ́AXδĬ^< GbڶlMzNIሩJZ΁:~# >~?V cЪh۲VS~_&ݮN{;My?hmZkh߾ƍ1aۖ{=wƫeeѯ\R/~'L`sV{NGy-82y0ظ-XQsԣ}X>[]޹85PH(aόy?Y#l{$AY>M݅yR[ s20v:dddpw}]FFse̙̘1IHHlٲt֍]ҴiSڶmK۶mY~=SN-~; ܬY>w\U9{OADQ#Q\r,3- ͖9ܙ;Q2{܂#Pd~xsys<9a fQXȴaqXhͰqvdF| fԱ,ŕ>&[F`hU"/twl܀n[q:s~H՛dؒ:t""PSbNpzu5qB[N{|U6c}nm-nܑw/1?D͵ *H?'1h4f(!Uّ?NJW\b55EQJZFQ0֢5L Zc[+6smei|r۷V(9 -AAAAA{O?Fyh"8p K.}X\Ѝ7Xd VZ,Z۷o`eeys.1x +}pޟ]dko*g̨P3 W2/9?IoGəbSo|N;P!˚ Rt5>~v1X g82H?FIد rvrHmK뽬^mg hX?HxǾĈiK=bL03Wk|7͉ <Ų3~VdEbF`Gu~z 4Dv+ hMn+f↳[rsy,"b*iozms,N!e9Y1k?aX(93@pz_TIO>KɜLۗɯUǢd]4Ѯ3Yŗ<rpl(dcg^O32{͜.bZrIJhڽEqn7Xe{T*bw} 2f,@zz<Qg1pΛH7r/?̈́Ny:JR-nol݈J[Dlږy|ݔpW8~{yyXקg^:#O(78W/o;V\s i^4yqKAAAAʂ;.\! IDATcpy<<<!µdqo~Әi?LkGEgld)5ͩCq\zjfWe͒S\MS1s%HjtFl33(A3Ժ.R2/ǡ /GjtMzFB |5_ ,+ Y(ҎAP}WndLx\#k<+zPQ`K㡣IxK~}: o$"3l7i?|ʕCiԼ< `܇-rRf;n!?5zT,݃6]cVrV1t-U3w܄ʃ1v2S|wQLlp񎢡^9F]o?Ei8e׎ Ƃ  A}wQ!;WMtzS C(7:E?3lIP0}I;6fЦaU vK+ U.*n>oPLvt29~̌&b  W"Z wDaܰD6酷v"߯0Ll]2U˥X 3r쇼6P<#?[DU Htd8_lp#Gdt߶w<ٱ"A݈iڍ 5dOlt$;v!o۽'3gDeH5yzO3={Q۝|7ň~\rYl-MhT'1n2yToL6PV}]bevo݀NXC % al^%.ub*3hp6|7AAtKPdY… ܸq'''bbbXfMpM.^,ˢ`3c}~ּ?7!:)AAoIςRCZqN:Z) UT7z #}=]9|H}XQ9|p//UJlg)wRF|ʺAT\-*؅V2y$kaBZ),:Zu$^^C\Kػ?ӿu-o3F g/^BNA)X|']~dEGARɢ prd//^Ř>zLx z&|M!XaܴoH;+.L_=oy3aֲ_u ^}7{ΞI'>. £{* q1hҤ qqqe.w^W O)M0}I:A=A\OFFͰr1ѷͥA)|t5-;wsi23ґPvw7#'z:FT &8"UPUMģg9w99Xh@xT-TIFUeHǥ'HOHAl4`ymd@\>{˾q)aaa v ň.fﶍܼzScQٰ3ܼ͘ʁDŵd͆xZnF`4<#hљҲkژؖ=Κ?2gAV̴Thݬ^A֠<$ۣ~;7'onA"7+͊-ʈ^i@obӺd\#ߓzYm03W0~ @a¨ H-y_BFp'GHUROvfJxf?'M`apkc>BߗZOaʂ嘧ad9 TT 9+rê*d\gXq /ڟ-Ob/s2~~2Q+/]9"rd(4O\#oaW2e\_]9G@rZ2}c*Ew&]>_,?^ѨU ؎ ʩ}8U-x P` PN;͛iҤ jBet;ɲLllz=tAA5l--Q$c:Nh#ĥ+9SNmsɕ-ԈE(N]ֽpwu瑮O;\Ekl7#a.8"symԉk^ 4ğG6bxb1`/IER1bDld PώÆr;M_sh腳l?sgQna5Q9Avl9yT;J s!0ZUɗcjiˮ}1ǵxh^aœ%߲їY߯`o15hЦhgWc4셣O%Vma>1J1OwFTkPqs ^4kdʜ%+ywh|m z/}5[)>i)gNQF2|x`⛃0rt/: 3TIFz6m{(Кs+"Oδv&҉V #S._>~XXۓaݏ @xDwU/_`ǥ '{|7/T&*&ժ0p.sGCQXrER)fAAּq ~Dn9gfq96{\bw7cȐwXݺud)$h{r)rXi?͛7cVj (8&mn֭_04#?$"'S`6I0pxtbZ{|8~h4TơTܸ֐F9z.9?o\ÀGIAA` /2a,YBӦMiܸ1nnn\rhԨ˖-]vH!-  5C?v; Gkto0=Myo\(1lZb.D˸[yecaJPPPV|Vkd$(y :eۆ/'l_9Q AΖt>OƿMu9zɜ1M(1YY舓C{\JF#"Jt ;x{{L_ |3aC'OI l-KFk=ֶ~jhZ&Mf|5#jUM@2gemnuPPv zuƹpIAL0.ڍ똛t~~Y~JngǸnځf@݆xnԋ݀G]thz{rr!#6AnIycKKKHJJBйsҥ &:KKK^|'  ¿:ՈH%)NH&j N8:8`Tʽc|+f xz;4ܖגqs֝{y}f4*~BAAJ^alccCǎ5kSLatԉ~ӧOyM@@:uBUUL@ǎ.  uD<=065'3;}bUspC|*phVlaiLJ s!c˿Ő\4ND2s\a{@ ̣ojh[&`^/P@oO2bwV9ƜMs/1ct UI֓s @)Ŋ_[K_%GP~;h7S4z*f6* cld|**>ܺɁ8|u~C{k8{W(>,)XYٳ{j]>[B l3q:RyWrPoחL"V#=KZodvmXHdDֶܺ͒?aoǷBVgjYtV AkfKYh>Cr!+;x~\N){ PX:3,:g6/0E2c[1ɺA1hshղ+{0{?5p@.  }Ou{,_{j*ڴiØ1cٳ'R/$1fZ-+W$11[[[w^.(*lI({Ӱacf^wVZFEĔN՛$&%pE쭌hR7(bˣ# PLZXk= kP.'Idcjb#fVO,O7 jj̡}{{$FV6nZ%"8#l#oGOȹ4iҊNNP?W+i8_(@xfya#&Z v8TH4f̱1e醟p&4] ` Cf,YIf\=8WZB$JO>I7f}͛~dxKScT=["m,LM|O)G9OzV6N]ayG;kFrʚΖbJ^CCHZjK$njoƘA]@8**?.'IfߥNLe Nn)' Bvۮ\իYjNNNL:3f:o߾ <7n/Ƙ1ch׮]=I6Ҡv-mVlЦKg#>1tlXĂ#$Ld0<<}ޱ\A{N *<3.WE+IՊq   @QH˖-iٲ% BQ>҈M6咃I≉%J4Aԫ.\%ڥ"IL( BH|PW"+aKWwGGANttiNCAAZĖR ~!}?6w\>#>CTy:_..ʼn6&Kr:=AAAAAjڴ)|L0cǎvZdYf̘14mڴ۵47GA&33 {!'; _$k̜'     = ?iӆ'bbbڵkYv-&&&L8rw?{[sL,pB]lo7т._^F4x>     <c dBBBP-A=# Ta=ĩٲy]u$#Yٱy},Cxڎm[C61,痘     BUYnە P8AA ۾; rpc+  _~߲07k d2 wnI!     /Q0AAAAAQ0AAAAAcAAAAAcAAAx"TՈ%k~%a"<4QU#DҎ_hۅ'֟e=A(  OAѲpؽ!U|wo۱Iju?s"t73?*UUfߑ\9L)֬Ŝa]\w,C^OJPxoT^hӎfM/9|&:tO,_Yo͘3i4F{x_=؂ O*Ϳ  3FUeo3@U1X[ZFʘ#yI fFQWamb=.p"h3Y=}1dY;GgrK7fymn>) _ͥyaڹdíc0yb<{^&.XSkx_=؂ O*`}wғCAA(gٙ8[SAt?ѕy~\|xC hTo].Y6ր䦿H,i3)'6ưȑ5Hbi[l e(>Q%N0LxXF}%޳ ¿Uի<<AAx sSc\]QOXmdaAO:Sʸq5bvrGU%?}{H q# @Q%уdgamaJuv}\:};h$F•+"Z}Ufk;MFz@jعxĦ]شBZ8U)KϞ}8q Yiyd) IDAT8S Gw_T@U5l޹ϐ-q5b)^Y)>cdN&Gn$Vļ/N#8W#*Ŕ΃%څz@QeKr= ^Ց x+?s0a@BurWdbomFf &7=ɲr"ޅZMP2 :̬szRo]!,Л!^x#^6reL%F FKtmJ+m.*99|b߬՚}ѻ?xCY| ?Z|ht^9zY.sAڝ߆/ Dhv*6[IF5=Ы'5K]T5CfyYԯ'GcDΔ]~Ķ $lpV@o翯 fH6nݼ(F,X_,'z2fx8W(g߳ݿ\)qmsWuӒcGQƭ{H/^O`(=AĖ  !Y%6:WVg۞}\;@qhؽ 7c;wJHR{/ĎÜ>;5FcFFvVvȜ>wo-u⚢W#Td.\ jDgP9pׯCV;Y#"H@¼IlO8CW:v:y?K-[b䁊ĥ+ڙR!z=t EkF/hֺ :ɔ9KV,f)SB=̩c޵F]<Wy 5ZUɗcjiˮ}1ǵȿRKW.8y杌 3YQi0g Ƿ,C0se+[̟u*Flߵhw3 ly:1*M+}KMkРM/@ΆmZ3-ɗ3y|t{7[\b{nP{aOgn"X=={SUg0p *͂cTœMߠK Μx ]Z*GVKP9tQAL-ZC᫃I10oiqaʢؼ| }:w' gzRO}? 5Ђ B鉂  %9dec _ uSܺz #d a_u'rdC0ëZ=WPq#jNxzz,^ o";;(ϱ 4 #04pudq-HVCvVxxelȏr#wGѲPMTL,*`j9j?.a y]4K@Xm-@@Pođ/4B-P*~,ՈZW&aBԕ x}t'¯ZƢ;#ϏbxUmU6gqcW%>?&Izۏ^v2yWw-U{+j>{nPa̯oju?.`thت*P)*Νa U}KF0ĊcGTy:)G2X#Mx7A.qt^DTɛ{1ODzB}~aoo/uG\R5襒kW|*(gn8XpX], (  j^杻ӳ~n宅VQĻ=AX3 *p3> W7rݿHõ+Ѩ /K/YRpuD4RdtYԃvq&W2&=-TBdc=&в xTz`K{^a2w㇥r2tBzR#RIcdxp$O]JFO&͹] S۷nPR|I1f,nʣ?sxp~:U}"N^LG+8.CO^LFMrxt~^Faa8gZrbgw$&blKCmb7.䚑?i׏]qHp9'Ub<ͧG{' & Ƃ  1fakk HBӺpt/tF5OƿMu9zɜ1M(1_EQzUTdYχc`d#N?Qct~I,nʣ?jH:S3%yj/({}\dYGl%2đ-|KV<잗<Ak{>P?"KCqŒU 5݃jql#ݿFi)+?wWresWk{cbm  3Ùc ~bt[+ P&ٹ$''WTRs 2S|:X T1?ALs WI#00i9ܸr{{' M $aa*\I0ٝ\=VX{!K1> GN_ EB7Edɀ GN9h6u-D!$,wj-sɳ\"VgnTN؎Y:+Nagirߞŝg$Ě𪸃tq  +}7juhɸ }UGz5_;D7'` o++{v;Bgq@^bƲ9dC'4jRUmz{Ʈ\Jӟf|5iS@Fyv/K֬s흾rTHS=Xԃ K?鯭/tc`w(?l )z7n (1-1,^21al Λrz}x-ldbE+Ȗ OȹԌm&=!|wƭ{H/^O}ARy 999H_S3?VU`8?%d)غpI[-α  ra4%>T_ s{zիD˾GH܏CH!a`$K8W1:Iλ6 {HQj壙'2jdYnjy7NEfVKA&I/ڒc(0 ,~T3;RF*U,5 H2-1=F{EI:;c]\ȴ¿NF݃(٣Atm:V1ד[aV,j ]Ό#~?e_/ca/ߒO O ) >pJV+ wBO?)? ~߲07k d2 wL0?;v,.]"$$ovMǎyw-6M+Wvc,5ZHq˸[]i-R`˴aq2u!Lߎ3LY8?_W QH9wC%VET'u4^Yb 6"t2Α̘oYd`0Bγט֌Gn? ΠwX:E\KaA?Ͱmu_cΫޣSs3|^~pmɒQojJ@AAɐ{> uV˜2e C СC׏zѵkWUVO6!{;3_@YX-q{ TI@m/]éf4WZݫ iYd ~w8>cn<,YQ㕷j!d"˂f<^6ygs9?_jEb鏱c]*x5=DkG$7P;0%l1a|VH^ |?Ydv3KG9oNHǕUf?~9|% 7u_GﭤճBs"'ݳFngbSS'훉\k sڇZ")ٻvKSϨtm%_uV:1=`SJΦc篦-^_HۿV痯>aO{aGxELV.+_~˩9Q0s~GG   [GOewԈ@qA`Mjj*)))9svŋ_~t#.̟?ŋ3sLl–-[&&&(SG8 dGש;r3h0v$M_13Tғc@惧fn'9>])u&Ⱥ)T%ë N#][ngm\|,^sKxk<5p"|4(pUWHUkYˁ7P\ ŏ UjFan j bٗs 5~;m3ʭs|=`l+Wd$,]M,9b2\d縯z8JHVš|~;Ɍ"snm_fFjFT-Ӗd^ ѕ0ξ̞38is7tl Yrx 0zj9P`,َlr=~|5 kս#CAHtk @lG!?NѧO" `\fMO566z4oޜf͚abb``ܹt֍_~۳~z֭[GRR˗/gqHLL|܌;+?˭hǛM#VѓKٽUi_ٽ9O_-!i"ڷy+4xRTkjDl<*wz7/FQs ِ̎4Z>ԈCC\/nd۷4h׈Z ־JR%~c_` jԎ CԬԩ G1k6hc:{eI*2&^Xsꗎg*D"qkʙ92JsiYVejJ37H˽=EpPU%gsΑ}\D_/(wjB!B!x!ӰaC!88KKKt:QQQ̚53g~zzAzhѢ-Z ))ÇsY=JTTTfr[Σ$_hq]~DQiy>l4Koi1fbPl .u,[X8taz8fKeS!c;2Q4>`imE0{ uuz^kQ /,EІ8cgZ4%{o͙Jmoҭf&jɉFiӶ%u]1K9ȵ>ti fRp,*.w-)Fb?.pg?Q-ܑ{sY*p18uB\sIJruL+&YkYupJM3YB!B!?L[ZZOn2jώ;غu+[l!::2OfС899QN^}UR UTĉDEE|9 R8>c~Ƞy??O9=(erb_W"ZKlps|"p%qBw>t:E/gkW|˹[!Vh8G~_Y[Up5;$ۑmf:γv[Z棖N(}It>)dsWӷ)1DWm"2Qqu֘-TsQ@Եص% 2|"Ocfr6d4H15 VNDVT cz*-Z ѐMX R1<0I I5#*%6䜖6[a?(e g#fS08!B!B)N_L޽{DDDPF ҥKHgMdd${zDDDp]-Z~'smO!-~){6GYS- 5.]oT*TF葥sػSIj09k][8o}X_A/=2xpu%*Ͳx<*l_iE}^m/]UJ=DeGշ6m):9pl**s,- ՞e(mv3{yIqBg|dz{ndNpp4e(uʘ`9J9{s :W, B!B!^z/ cWWW.^HLL ;G' lll2rqqɭHtgY8nWEԀlg>+g⺳\*jR㓟 lXE`s8Rba,})^vtV+%O~Ws&/R01.%"]f(71eptlJSXq3gjڙc1FNY1a=)CihԤXT%p4Wxu Wl2.Xd`k(1#blx{y-U:E߹C!%܃4o?֛9ڵ/j:#xDshlU`"c[[GC\:]7ޫWΛgfi6Nya3{mgI b,B!BB&|2J*p]^^^\t %J~ǼtXЎҙO3 պ̚EQ43;lnb~/: : ccyj="q3Έc)k`z7|4Nya-q/B~3E8ŵ&e>C=&n9iPzd,u_'})CXTҵq/@Ch {wqⴘl W=JY5}I+A4ê{> IDATҐLi?}iPԅ2QIa$̯'S[ԥd% EpNҵuTkc5{4PT%|9~V)o&15+xܖa-^וF9_10" [RIoGU8HX!B!?/v5+>7o'NA|SZ|ףRعs' zB!i*be H3~; H~!gp) }Mcgggn߾z3C>}#uU2O#D-5_,B!B!%B>\r(˗3pWR|̝;wr HEuczҶެ'z}ޝB!B!ᅜa\Hʔ)éS8x 4F[._ԨQ}PLݏAҽ~B!B!"/ cZjeW]EQZ*k.N !B!B!Ŀ 0~ݾ};brHMMeǎIh!B!B!OzaƁx{{9Cm۶&o׮ *Ur= !B!B!Ŀ 0x7~YՕzl2ZnJzJ=B!B!B 0fh_J;׬cKzn]9jUk`pAlYCjU*O~C:1i78/ayh_>ѝٖ1&n*vN !I駟2|p>s2h7o<>SBCCgW OI`0rM~mk,vr\X!BuA購Vʽ8j%7_\/㿃{cʛ#9MXlׇφ&k]IPh&EQR aK0hذ!3 &p)֯_(5 zVVPHL&:$'RHBmKQTpz:D{w(RG!B<vE- |$a 7Xо?:T,Nذ4}я9>52i:-F|@ExSdnHl }=ЫeHNUܼ}}0;q3W0n }P귃8ث7%+ُ`TXՑw:lnTc.R,x˛|Ъ_7޳Ǝ-}R5i>'V3%5YЬcK>Xͺ,1 ~G04|h1okX٧3ir 8=docyB1l>fQ\]g^l@Kґ_u5s"WyJ¯;}w;|E7/xMELL %8v-ͦUx5BqP,̏vrbqEڴ`2~,NN̝ۨ?6 Wu˹w:֖fxydp8<*K~wAYs9kwnD6M.]l-Z>0 (&OYLTLPԷ8g7m_LoÔzTAR] cWy}{6ȏgnUyCG9w fΚtoRz3aLOn;$,x$ cnpe{`4*pr^^|p=O@ퟧH&8(dP1{c-(yƐ<;;;077/N:OM;+̋pe\ }*OM1^GB!,ݸQ˨X{≽F c 'BjZ5_~5Ϝ]Gs) fHNo]Ļ`XYKV'Y034+{1cEЧkW=8pN h]m#2,FHSY07r%! XJ]ɱb9u,C:lxm=2my=6@hp(s7@Z]s[rI7 N#7z Kg~ߵn4)zr/>qzHӻsz];6Pi_E,1RSodiGVm{b8 x1aףݿG=%[2/r!VkHZí{z6mXȗz3r"bcf0PCcסsX KW)F8z*l;?q)z/:Es- #~blOP.NoG{*> xhT5mDCaC}O1l/ 9&b]>scxpnGݛ(%05;~8t-?~w##!={=-^Ai*ihσ'zf7| =pF I m!c*HT[s!WgNQQ;h\1'qa Cr7DJ K?"ē^uԩCdd$Ԓ5TbIeoޫ7j\)quHXͺ{3\o]܍m3SK5`:mrrӳ^e4R׃JUJC|_ǥsGnb?@M(M务x-/SQS'Q--s$4tz3C bal)7ʇR1,Q+RY J|bNŔiq0O/j6YHͩzp@xT`h9w4G)ݿ]"thيג8u`+"r}*j6Ī,[A*RZ7X N<(KJ*ghXUA5?ͧכ iVN (yUA/\>.Tzpx4jr,_'?R;5CI8phvRԜ? !?Yܟ|ݔ|@ejRLXs4i҈>Opv}ae a sEs73MeB8v Zޜ;6Ф~MuϨ+{rA^%̹]y>)9HL6B3*]4-@?W=<}Z2wƐoi֩>SFIIJe<;2#>.ky+z*'>}>Eڵhzsܽ0-/y&kۛSRj3KV"kg)MT mƂ7? k[ydq66K=R _|4>y%*fFF``8F(noex)xee0(ڱ(%̨j\ycc:FgUQ(Ct74sl̫7PRQ!jl^fq/%״U8~x)Z'= kTz-/^ArL?Fܳ:{%!ń4>/ ).A\'/NeŨrmGV22zFƹʘKntN5EQrRW>s&fpl4"Ӆ"N(9Ő6u*k={`N*HbceMoʖ`Ȝ *v~l4BB]9T bނsg4T}W`T3c\uQ|'s;_s(+NJѓ~vy5xB!BhE};ANVo{Os`"损ʰ|3sF6TEw WZkn\?GhfO$U|Kjk9OW3R%qq&asW?RZ!3n"@Vo5q[['.]DAԯ^_j1vfع b M U%E9ob秿kRϛ*5sO{y<[??1ZoI7Z;K]pN}*EZC~Ƴȱ 1>Vq% 1?a[fa V'Fp C,;$=Kyrs!UCD80P] f`X(뗞|-QKly=Avª4f]ԯJjuQ 6JuXJz/?HDH$OK]őy:5ԭZwO֤X^=?}s?#sm6+w/CP;PTTu`Tv;ײP"mYiҼ3S##i/u2k1u< :^Y%Fg voߖME譌ƈ%q(F#$R?&&V$<lMC^lz#Vt,[B<$ c!B!^h*XGqnѴ7ߒ6z7.š[1g{Z] +sԬ@M0 ۩EYn91?L[q\<}xCRv7:#~]ޤTHяߤF&p:b_L |jM;mwi?"9~6VT,닕sVRiu~b?LLO*'%s 68Фj:ȘqnǛro/ŘZӯR3?D0Pz vn\@W듚e&z@,{P\>$织e>O/]RjV.b!}A‚ GN>zB@~oqy`AP8ͻ+ $ѾOW>Аu_[^EvӼ>ċi*be H3~; HB!B!Ŀ_v3Ҙt%.E)]ę/;3ϣg?r#x1HX!B!29y[7]-4{H5{rF:cƃG +菾zd !^$r,I!BɒB!Byax&S!B!B!Ŀ_ ca,B_!]  K֬&>yѨaXn \0/^iINa֒ey#17`@> `۾;7aabdTffzC4> QaY6VG-»R~'7Y| [N[8p ޠfN*6oXI½[vOnxT ZϘʼn=ۉs5zث7%+s럣oӴ~ˮcJ=~5*VvB=dqYG1yb5brD9똿\Ηc|6݉FAOhƺ_r vVZ:hD7F*_B$B!(fy]^oq,5J)[O\KW$[8=.c˥g ;>ՓΧd1@ʂ n B8s#M?DrR#T{cҌ96k=v٣y5ZBo'޽ sO!=s³=k8B! FL,3!YlP>ظ}3'NFSb`ܒZ\ʬuuNxz^/mլ ӱo= #eKQ>$Ԥ%'b?@M(MaOmsHݱj6Y6 t˗/Pzt!84oEy{mt0\!iKM+X*ss%<vx?WVu=93\eۢ|u:p{kBјV2 τ ɣd$L)2`?t.8#{}`1[ב_R hs"zSxNjHL.-G.s7 ;WoBedj80}4RFĞczÁêmBtgfx[݆GK6;廸Xҵ:0샮?{b'qΫ, j& &it5Қk+3c*>yA!suG1ܹTKW֧':Oܮ_`̹ȉ($TdQo#[Fv̵:|!6gي C⵶})ԨrgyKɹUx>ަS֣VLQVJG:3q5c}!.qr'Eɹ\N.yj0 zA`0b!M/ڵkǞ={ߟݻwӶm[ϧҦEwR g|,~S?[ƃCE ݻ>_i[UT;Wqgx>;tk&;odcMlF ?cxT!L~2;֊ s*kxWtCe_?ywD}2g~@/xhm_#@ǹyC2 =`؎ʻ,co B!^ViTސU[vw\p C,;txoI7Z{3#;z'O: ,{ܸ~%KべU'iTV`gyE/"֬"-}KpȾ'==.v=7sN}*EZg>~nǞ[ OS۔v1z9{J=ٲy#{l^f<1rB!K^F={h"m@`` 'O`;v^zQvm:vHxxx}w;ta' > ˲Wa3\x8B"$$+jF.im?΅fC?K׬Kl4p8_u(U*'qfƝ>O~XgB[a4h{Gh,qHko2gbPm(Qyz [UJӡGC!x dbccw.\`޽+W`kkK^h۶-M!,Xŋ3sLn֭[RJQti"LaV.T7 'Pg:>-VmiJGr'UZ;_, g;rϕ]Yw(U'G[ܲ[8{Qfץvd \mZmj Wp4xXS UVh . c`Nh0̣?'>0_ >w{Ɯb"%4uHwx+݆Mbd|9kV?@LA,?qӪ `E|݇^Ͱ Z >gw?>0k]: O7o1?ekpo #Y*fи85r{|2 ~^ьo`CBa27G3fLŋY؈kxtÿy#Td1 Ȼ}Y&̵jpv+w;9-'Xiu+FHaVjݾW g}01*ow㫯3rXjMڳ=sm-XZ.Z9 jV~& >ѱ ,\W0Ӏ'Ev; bO_B1[+ ҁ 3y.џ\B!-f WxTywV[~S=Hy6v\j-/yS[H$,BWdv5+.T ̌P4iBF077G׳pB:w($''qF6lIM}m03m;ǬQ4ưk|i6_ǿEZruf6~*Vkp bn'vtEK=@X׽ɧR ߏӼ2#9Ռ}IywG!x"V$ 1'!˿r4lؐ |||2=A鈊b֬Y9sӣGիG-hѢIII>|grQ2(lr%װB3z?:4eG (;W;Z)9^F~/Va3(k& Ŧ[[ˆN\?GQcT6u2#?E#QV WP[pۚ] m3FL=עA/{ulP?m8u|n73WKN4jM-g:Ns61k#|W ̣yX U]&Z@S`\|2Z#U|/bZqꄸx*W۹rh%PTq0YB!R8s< k~*MIͻB!2aliiI||<ݺu?=ώ;غu+[l!::2OfС899QN^}UR UTĉDEE|9 R8>c~Ƞy??O9=(erb_W"ZKlps|"p%qBw>t:E/gkW|˹[!Vh8G~_Y[Up5;$ۑmf:γv[Z棖N(}It>)dsWӷ)1DWm"2Qqu֘-TsQ@Եص% 2|"OcfrCsFPiԖ,V۫tq5LkKusvTv/^.[r6s|>VB8IJ Q{8+;0[Z-ihחUk"qzup{DekT ƶ+8رrvӽI{pa}<~ivEF^rj EcEI h|Iz:W 1*;TWW =~B!FĢY*"ċdIbo_tB!xKY0vqq… ܽ{H{ȝ)' c9;;-)掞 sEhv`h?0kԛA7aRbD}(Q%<;/傕t$aCXx6@Oh⯟9 sCTbS+Syk~@oRGĦwEm{}(Fq,䠁\7,+ӪYInvS(UM"={Հ̙~ *QFALڼWvͧs@>t 79$MٟfApxo!=U^(%͢9js/ՠ8?FϭmŵL?UfHî[{ ;]&R?oƲikpjT\P~)B!B!`\X1._ [cp%/(n>Y\i;5Osu@5ݙ>qj$Bv61\IBmH)ߺ ǦSyr-kf~?^RP>vXQ˟꽦C jMeH _ z^A3͊.i,'~ŸI=5{VȔ}`WT7 yԻg4k.c'̎>8e547OIqzo_y<`* J<pƫA?꿕c0m0y13F,rtfXA6=33z2܂ Sz=nA^S0ɋ,)H=8jN|56c~kwt÷]~Ty) !B!B*iw9PJNYf1n86lO||A}B_\+B!~ڪ%$<0r'O4vrr۹䄭mSNe-^VI\?yx8V۶aj@X!B!BW@@/_Z˗ 888qos\ZN5#n3o2b !B!Bx)gɓ'aÆT^5k*N+}ƱxIi|xex_tB!B!Bd㥜a PfMn @hh(jjU-[]v&)B!B!!/mqqwǎ`oo@JJ ;w)B !B!B!xK[0ӓ&oߺuk6nH||B!Ba0h0k& mF~?ZQXv=uT8ylx ;ga0p*[T'mc?B!DN^ƏhтqaaaڵkYv-7.rBXq]v>;wCtЁv;ce_@_֨#B!D~{h`4mъ&o7fCEQsin\::Kºa鴯7CEÎ{u%f,_kgcy#qol3YǕS=Eo0c</4M~G!+7uh"Ljj*cƌLݳEbmvuơh &5h'[Zh(jk+m B!wufΜʹGPRqqrQݪ.jRU\=?¡(,tq:s>%[VǥT %SR}2rB!x^ق1@>UD]{1&SÕzu7glo4!Bs_WԂAcnW WnbZ`0ؑHV"ZF_|RB!y Svh0CzJCgF{0=Z ;wȱ}N!B Wm>%.!fxFol^1(A.A!m NXvbDFO eބ57pimޘ}:5.a޽oWt끋WL9>NSLEn߻KAR"U} .؋%< u`L==#\ m nv,qA!3Kq󶤪,N\eQ hF٠D(fE>3clNzuyw(X,xa oJwPJtxK{H@?EP" *N\o˶_N$hk8~4b!܋Sq<~Վ5п'뼅3\=K:䓏øtVurUBC&P# y}GheI֥*c_ }pŭ[z8%մ._Ϻ^9+O[T~H!{JrK@&9/QbuʺiJg?qr&εF q0r&1Yù3~,f*T(J`p5;kR~w?@C**V'9䘛)) P(_@:^̪5KJk_w |'r8Ϧ8ؾy);f(_ +\=9&(lzl],$R0B!"_h 5c^֭_ς]զMZ̕SbPrzaLoGGבԣ(0)}J"h1(8,J8^ WaTXܻ]HQ5#q-]QZG-l>p̃ۧ͛)P'gyɜٴ +ƕXkxJj9>Yϔ6;ڄB!DnIX!B|dmQթϋ3iׄV8)Vk((*h0HbѪ)dítmմ ֎8g_Of^i A7c꒙(_$nPr1֔X(̂Eݺh̨W*,?!4Be(gr\ْap&+XS۟x:[kd]cReC3pȇRƵ `o35U (ރ v{r7գ\ ֖4x]~]6\/a, c9s/< 9:.ZNB,=3nVz5 چ,;-ڂ5*Ѡ[*KNaw.j|pw.kJ^yLð7I͔V? ;[k*-I~~kfdڕ^Ha{k|؞by3~!ʌ퉂q~k(fMXh5*S )ti۞)Fm"ui{JeQPkܘW̤vْ8(5-؇]ׯ`nʔ'HzbondHU GԞl:s ǯPtGߜ_7&}s XtR +s3+sҏϔ6Ǝɔ(@Ba~fhw9AB Xx\s+"sĈPŋNG!~ڪ%$<0gר"OZB!^ez)!5ϝC:ozvB!DnɒB!B\8B` [R/:-!B`,B!/3M&~B!kHmB!B!BׁB!B!BR0B!B!B+_0w{i?Y;`xѹ!B!BW`'|INN~Myȵ;s.E璟 W>o.OǛB!B!{e ߧW^DFFo>z̓L$,(sΣKB|7S&]àzG@jy }FFd>}cKխ9X:oфӋ0hˁ_ңU}B*RnssIY0ǯiP2AߤK?Q]iR3,;(iͮ>e*U{f?&e(1f1np7^sbRsC.mB!B!gȋQFqUʕ+޽{ /ZjO 2t2y䩅H&_y⨹K\}?dձJ}Y=:Cȗ=GwftO{1rTmK,s~K32'%Ɍӡb|jWE=d t?bZ\cONC=>bf8g^>\̦rl+ipj8;kf?A1 (re?L,]g86%&w} B!B!w2cEQ`޼yl߾___&N@>}8z(ݺuvڴk׎ T*U>e`A.L1aiKj1rh\x:;~XPL*-:.ac3M~NJF=dOWW~˸Ei2_Vr$SHیo`!+ޜ0-o>0o\ʆH,IvyOGvuIO2X8EfEk? <oGugr$v|jâ(N뇳S _^vwc7rGb84-ǦŸFΎ_1mmgn(S΃ny[T[l 8۱$bK洫e?s)m7a2faLvK7cHRSb{{LO#Вb@Aa\\Z }͵d cY y<B!B!x) IIIٿ?{ʕ+ӭ[713K;9s`Mƶmضm KK˼%fE=n2_|v=hX5*Iz4.(YqݷsRkiFrjjʞ9VjҊՙ^#^Sn卛3tjs, %j{j :r[+p0GPŇǑl*UOG1:Ds Bذ])]T J .`!e_8u_;a{xg kGF@"Qzz:ϐ|{Nue݈rhawc9Zǘ>3c& e*UҒ79m^|=k[ӧOvZ>ׯOi޼9:t3gp6lؐ#ϒϰy9TVVDf&iglsj}JCZ;VԀMA7mYүT*tq]ú?e\?S03捪cX* ՎOG iT5ɆԘ@ З5)vu gزz-ᄂbF`M[vN`b9«B.lflo܊.y'/LBW᏿k@"aSX{KYaq 9 oBq:t ECQ:[?9t1$= :4~9x?˲B!B!`leeE||<;wN]||<;wd۶mlݺLۜ:uRH֭K:ujժTZǏaÆ/G@2f_.oGď7_?+LmL:6Z+읊QS? o~mi=&b^Ω5 eʻP|%hol]6.ƩffٓwHp M+_Xm2S*>oQ5ai2]F3N F*B!ro}_E",Df|3mwo^nn@eh3ws2,|6{Oaܑڑ7G>Wu{Tyj3() JZP E1dQ0֢5 wLDК]:؏6d_6An)>-OaE|)[3bC B!B!^wydsdkjLL ϧzٓŋ?U,(::Eѽ{wBCC?>@y+tF̼_a{WR/s vV𥼷'%(gfE@k\snT*T'VȐ%H&=N¨ʹ挱[g9oG)c]lr}(Q qG'Dᐮ8+dlG&O Z-ihɪ5lqzup{Dek4e1SLTXYB̳50zJEmP;^{80SN+6ZҔ+<%5q{w42J>sJ%zC)]W A!B!B^...\pwi3 Fz` =sNdOwpŧ'c\O:fxó2`JD=ώK`Gr<GX6e(jc=, P@맹p:glb\uUƄXTvƚ'P~?)EQ;^x9~6Ѯ}h\2Ʈ%9h -}2WKMʴjVvss'Gl%J{9czs{5.E9/s毟aQnS6oա]94]iyM>(IӷxgYܺ5~fHOJIh܋i5.*M"skl~qmDEg~*O5C֞??N@ Oܽ oM IDAT,F) aMA !B!B^ʂqb|2nnnyK(^xfrx?)q-tg 48 Ĝofs=& #|2prGkNIɵYtq Vn{KBo aE /̓(51E }'|f/={8z@>xFNc>+MكD\L3ZʼDTț=æYs{>)fv(pA|?L+{ϓS_S⡀3^ Q(wW&l˄ɋ1b9 6 ďT6r*,1ד)L7+T5)㙼[M}žHWCn3o;?{G7|KڥG~B!B!k/vǞ)5餙5kƍaÆ|8*]vakkKٴiSNQ|ߪ׺/g[_5v g9 Bg#'寽QV,>\ !B!y߲j $ =D 饜aɓ'5}v899|ԩL*'OG|j6Lm( !B!B x)tU|yj5/_N]ppp<Ν;\rZ-㗝"kFv%f e(}I !B!Bzx) 666xyyQի:m"##Jq,^Ro>m7"S?p c,G!Bg`0ʵ߷~x] Z~uoX*_We9l}s;buk=fM/T3.^)/ef͚lݚvaVZZjlٲڵkoB!B3X'}iJ.c'ӴE+ݘ? }~+Osҩ|U˘c哪$[N ;v!֕,~]=}sƖx}àd::Lq}'e7v mڙ#G!`cRRRqpp %%;wB!xY܎- /:?-\C1/~# GQSu|6KNGnˏ$_}y<]>jkttFy1E~͛Ө4y1ۇ7ùw9\*aޯ5}u< /}Qg!OOOΝ;ǖ-[hԨ8p[n ƍ͍?ϔB!%Ko>>(q&Uf%޼ػ7-Jn=phrmNϞDB\,h޸6a{Wgg0hntGhYݺS\%#3ll⏯bO I֘jIv-Ѯ՚t`Y2uk")l,( Ҝz$%Ǿ0ԌӖI6]Nx3.]0o+y ܱ;7aibHTjs._z_`{sM_oOv숫O`z95S'ƻ\9 l\yLݺk`sݹsI@v翨wO#?HP9h  &2xF,;3[#ϰdܾ[1'ul_hC$t_{1\I`0c5f)1cke{f56>LMo˜)W~O߬CbŖ(f]R\.~f3@K_0:u*'**ڴiرcsܮm۶h4lmmi֬ٿB!Ǝ K\8]>S6đ;iLL!?&- }9e>=:u՛ytz Sڀ3qj<>+G7/\2g"R ##@7|?{e(whTu^%뙽x9__jcn~ q3Wn'm=~@/OFUۢ XH_lL86OP0@ű`_{R{IK6znt6vODjj~]лsKbN>0f|^y>[C{A}܏{q:OҵS\۳9?Κ.!&k9Q3i*-Dv)V"ݥKgAm nv,qA!QĥXMŀva MtJ2sV}RՎz{l`ɚ3Ǝgd++\'%܈e浔rIgn/Ӯ?S5 3 <ܤ~.5.am/R߷0+ݏʒk\{uʞiP+ٺt"]۴wo'É}U3kԴOk}>]0~.Yےd"O/uޞpOĉ1c[fʕ;w.m<==iݺ50qDñL!B;_O`oHMܩ# ^ukv`bӟx(8ؾy);fӴv+Taşk&&NW;*֨@ޟЮ߃]Fc<[qp+g߱'SY~׳3~(MaO>8Kga^'ܪVh px$^!o,8~޺TT8v,+!مmq+喞pj=V=JmWeP֭rϓwlu24) C||+y.C!Oqj1<ں(Ji3&O\o˶_N$hk8~4bi#rNO, ~WJ˯eۧI?&`*5jMbsg p4RbEzza4r-@ 00{t;Fƚk;iǞ͵jV+ ;O{FKHlm\*F0e?+:'܈o'£O,Ni PQ}/ /_f՚% %s}k/A4c4^9OměuDjЩ2޿˜ѻ&,Nvc޶tr,B2WC, 3߻& K ^1@ǎYt)X-Z0l0:wቇT* Ve8p:v/ر;c0$QÕzqYb T/86Q9~bmWijԬ*_3:} {&Z5^ݩU.fZ!B<`x\&RT~vMA,)ԯ߂UN{=IZp 48||!Sڜzï,uzݍ&ڄٴ +ƕXkxJj'g[eى9q\3[@9+q>VkҲ%'nyw&pc-wu)1TX94qt|z:MƷ~M#U0)}J"h1(8,Jø|k3gۇ~پ{'os̒oP=S`7J7a4]&4Ic7ZWc8{5:DZE\'ԮInbP>d햝|& P\ʎ)c?wÜ~@[h+ƮӬu 8<#Skϴc3'wP'P)Z.5g)2o1W#(jg!/ӷo_FɸqQtڕSfj۵kWN_o߾K.pN֨m!κu+iڢ-d>rbR*ڿ}j:] (P۱R) [o}ށ@MxB!Dޔ+Uofz9S~'ZYq#$ihA̯u<HӠd3 LjV_gzHa_J6#bf`:6EHT .e22ӨQPaP FsSS\'NƘX~"l)Z;/L9RZAӣ*:v5?xQ^1@-ذa9ӳgO_@&Mի#GGpp0-Z"!B!/Zg,_d_WӫeԄ)߸>Sd1ڂؿ傳|snJ.赶ܸ~MjSh!.R +5(4 aoXbeq(V|no0ZubeT*07".>.SDV/¥+PC?wp}+ZO;?cLy䡖c,ygqtZ;L}F`Ή-Ncft=ǓLט"%@jrO0\f[c/[!{A}v`o<:33]vS8zx/==s~=(/'ǡ{1gR?qޕx5_3ױJj5F",,۷3ydȑ#&OPTL8۷QPgY^܋M }GULK $$HJl((VtEk4EQ;R@HIHD~xI23'wέS 1~df^0nߢZ6ɯ;2ڷ6]ەy܉b1"/""ToWvl]ؒ2n߹7_NxGWSFQH0))?vcàYInԜG3 8UjIЪ sd SZy]76;۵α#6Α>zfHJjȬoRÏVZҾ]7fN}Z)<^v'}V\Vޜ_!v#fc;Wp=/>ڬN}8|U1Sޘ!QASVAhhKVoÞmDԨ_먄$yu|8}YΘfyw$'#B-DbX^,[YW m}xsۏ}8!H.ׯr`63vwtF~ˤmcʕ%juuyߋuxr<6Ly>7sֹ;ՖXf5ǜ&fŵ=>27w^yރ^{5*b "77s*QU/?ӤIZ2uӚz? XnKCDDDDLWЦAjrzoGٴ?bB6kYg'5>:u-ǸydM`94a*aAԜ/ǰ2Rkp>2q3a#*2Ȉ,جNzIsK1Pj6{+Փ}8߮o{o+/g/OysuQs'}J1Ѳ~*AϪ'xoG3 9WzW`L}8|U1I}t_g˃ٹ6CZj{I lvyz?$%TV/{kޟX 0Ve.ᇇnJZ|yjfuSvJ\Ňi|{ecfsxs쳴iӅXp" AɌ|EfU12bmiZ,.};o[5~:DzhX?,`]U]yb}tɿXxS,*| 6 *Rλ1^DDÆ c祗^[nUrͨ عs'5Ż}O&V5jV5-;F;m |u]b9rȷ9m-?^;a)b=r;-2Ҹ8|xt>*R˂1@nݘapGԭGDlMr݌8)tN?EKf\ ~d>Q'>37M~M% 4NjQ||JTV7^>1جL~ksƗyhxx0?. :blm 99 &pT}C Ea FQ!ɵޭ ̧_P"@kVYX@HpRС#Xnˁ|6mVo:ϲλމ;ߙ)P7EN}tE0|<3&Ѱ%:s0>f4{ց?E [߭ й|蝵ү*""""r5xjěw-}zbЏKNەWañVVoD_*zfmO=Uu4nG~ʾ_7zW'Eu/} lôpbevzU&6^уjx*3? y%ȟ>kG 2jdQV-BúltM ߣz%KhIˏ,H:*lw դv" IDATc_wVz2MO| ?0?JLd8prFˊ45YwG9ǼW}< *Tu۱u>Ck0jk2s9s뷳TOkuղ,:7V͇Z1@q?h9#RT0& 7}M_<:R /w\gдoM }{Đ[i&[v+nZҢ̚%m""nmkc93|c mv ~<}KO]W>jSyYf2z:>;lOW j0ռuT͟CvV&Sks]CQ/ç8:n Z.~ptYyA Nw#F]yS_J:n,|Us=侻FBZsqgQWvHw}__Ʉ=A\z7^y4oᰙ4nnv'1 ;?żY8|`/!~$թŃC &Eq`ߗ?ƌqq9%k_F73[wУ_~gp^à ym7m&fM#~⪅#߰5nƇ?e{x? k2d_Ii{oX0 Oy?\":,ii@&`/nQ"eѰ^-מ*Am3GȈ'_;1ǵm4UVoYcܠM%czm|FY<>}\[QQ`ޘ%Gggwxo"EDDDD*ҭ $&2N}w2>>枿"NC>{|SYoy?\8Z-|1 <͂S=D J?u]6xVvMZh{2m89gc挱<|]o.6L 6ۆrإeԭݧ'?f9"gdBrɵ3sЗo.1_` 72m8yɽx3O|ɻ8"U@ת/eOo $8^cQ9az٣\gykobf.ٗ߀pP|cy-V4}yZ[gN͚0 iF mlݼ=/) """"":&11-pv$"2b#]9+W.~+Yz9g3fƔqdN"Қxb_W?WGB͡$yٻzmLG~|6 n Όj{OJsz ˙2ڭҵy 75 49}ϾȨllz-xK7﹏nϥ7 )qSY6OrjOK.jX'H2[2`OzR0u]2iոwf#xQ~~uy] >̩-^_4^\En=x0 KVi8<-"2۩Mٿm~,r2١=L|ɟ~Dj jّ?Nd5^Qhw_04p0H A`BEJB ܎2nY+U-ݷ3z4psM3w6QGx- $XNBNhrylQ\j5 Ǝ];Ov~lܰujIi btnDםZ+#+'u8۱X 5oHL}Nï5\0֭]NV%q<֭]v5O1)"-uo(rUlVȠ0?GUt]kOcuB(nI^^~qkJy*/sZNiUOo:ƞ5N8J~ݳu3 7t&nm扜_%y*9!|4y,w^ Nvms-ts$&Z&y2vr9-ܣ@àYInԜG3 XA\6De>=䇮w͗b葃V6II BjcQ#JrZk1R,:;U>*Wz>Ivݘ9Uj|L\r3(Xx5a^,!{2 .Zv⭉ӱU> }.%y_D+`R[S`ŝB}=ݶl^/s !VkàWxc҇DM9[HJ\\Vޜ_!v#fc;Wp=/pxλbŒg u=梢kir;m<_j/H.ׯr`63vwteőԠmq[y\DnbQ5X&ˌ:qA<9EvMC:*ﭯsZNVU2?{vD%JH"(^LJǑ|O DyfuҫUL;_ $ŅR;5[']Cٔ5QTOr7 L9&L%,ȟ^ӳ49tfv?6 Vֳ};>ͦCJz@;v)i-BhY?'ۃyg>j4y¨)iڻ ?;ᾇ5<| `d s ɍw&B^$'SOS~.NyʻdyX0g2xs.[ E|"#kA\j,v ?<vPS3 `-k]'ŏ?V/>|M{CS W纁giӦ )gu:_yzz-^߻4x_ Ӈ?M}*rW:%ȅ%hVXJ {rsXzӦ=j>zy/D{U>5Ԫ9D ~4/<}soһ;~  yrO{>P;EDDDDDOb#2:Gr;BIHI}u[R7Ə4riVX8+Rrh _ `,"""""":ic-XG"$N4|`NsNMkSpU3#uHSx)hK  9-)e.""""""""""EDDDDigYW~L0p?~9c1 ;ffòo*T09`/k<cc޿qv>SEg9`,"""""""""" """"""""""R~PT͟CvV&Sks]CQ/pwg҅dvn侻FBZs¤/瓱&*,kte+Xff~'"1:wv3M:¨@LN og?B 1stȟ06F9Ѝ$1asbbc庵4M gpk8ED#GA~wWqcЀxV-PL/3Es&2䦫yHlȟ """""ps&[hѩ; Rx OR+&͚ѡmK;ر'&Ѽ%kkOyIv h?;ɭlR?:-C9i;=^OfMxÍ8"WuDDDDOJ[RT-B~!_^]] .7RsjUiXN6`E1eg#7&e^fu Uʡafg}@`,""""R LLV>x1MKR-:s6+& `l^zqn݅CGxa܁\(<ކY""""rQXDDDD$!su^j~I͠qFbJ/4PXdg%HIf({)#+'GcJ=/wNr(oy,v.$q]CMi\)ʂkxvnHqc /G;[5_#Kh딳~[Ƞp'Q~^s0koɚ C {hYfv&͜7_fmDӣsk2^ʉۚ:u_b5}]ySUcsRXDDDD[0ca[I0M0<O?I˟o"?AFXL oHd潌6C\>AnïRbJ{[./99}m<XDDT0Ԉ8ig'вv(Z]RnUkپgsW-mV-Ӵ͢%+ǎ` 6&m[r6/<Ѣz;{ 㫑؈ ~&;A~v7n@&1-1U۰1nL͚FΡU G ak 3Ɲ be=|e~a}w|_X'2jd3ܲz-l^ysԞU+eu=iآw =[׌"?ʢsʤqjmk5exl΀oofwr.sm7ӤC/ $ķpв[vaq[m& 7}M_<:_?kN}ga'޾ gc[رGzz3m$x̍qX~5'cKo҂Vc_n,L{>;lOW jcӮۚؽfe+cm{ʱ<ŧ0`,""""ahbrV :#(%1glnn #ݙ{I n8]֬˯p[1Ԍ$[~?XHltՓabaw^۹N-"sڶlCplٵo/&>.и:^c2{wQ?c'-O,"k4mXo"Cbj7`OM%Zp9xvĿKHj_i"?{/-t߶QCl!jktE)>oۖDX%Wކ_~F5qac`^cy zޝ+8*:Ơ7^&^xw &Ibðro4Q1mo`1 _vjL@O!`sy{>68NZRyg&RR=۸~ۀE6{|}]&-&xCDx*Ri Mlky{_YkS< """"޻' kR弼\վItؕo!k"j5 &2L/8'Q=*8ltL,?oMjt:!cbaFipZmTϛ6n%)#vfҽ%*YGx~t{=٧j IDATeǎ.4K v{BB:zo:yӏڰ=|x WP8s̝3Co!Swanؼa1Q (ngft}&ФYn۹fM&J'|CW^ex̯Bbz_E-3 ?ፇFx%k!:gNS! =XM E"6nIz0mcXL7}5U~' mĜٰq-i~ }|ײba2G|Y۞rl ""r!SXDDDD`a󎽬_هpX8]n\:$8$B wZMC(tST2vZxgcLl6lV{s1c}j8E?NTl?[Epsul<ʱB _~=!ZeA/d«ϰrs( yilV' Қ˪]l'~ܱ2yLzYÄ'VFKSq+of̛GH s<>"{)F|B 0 -zjWFqNƌ۫^bE1eg#7&D*qu\ˊ[lu_9k1U5ED xzj%0X zwDpL"oHP`X-L0 .gӲA]۵7zZ|y"BC&,0Ow,_H.?,.V&W<|VWGYB'":*8z~vO5=>@W1[n.fDKV>x1MOI(8=$ à佒&I==D6=}vo}x+ǭ,g \n n؁BvѭuN35|5j'5b.gq터P^ F` ʠ[w)?^xzw,A<%Qؼ[Pu_bU1߼XzXvd{̱<ȃ\T0Jua'oԬҜZafiڰwᒼT^) 5p9Xv9Zh/V{V+Ԉ27`>8֭[XL$$!=]ZD븝o'޸0[ɩ%H{ؿ/~ai4M1Ҽ~0>hBh\Rs#>U1lA~gq<^tx%6oye}9Nc_{DD⥂T Mf-lٸ6ڦ9޴ܕkS?4[fֈ}_7cZ $4YDiULgr72&4g=cÃx΁'7ϴYȚ4@?;թ]g۾]JQa^;([3tC8+Vw履'|k#2%7 L9&L%,ȟ^ӳ،MX Of/U7"ײX6[6dv$2V Yp{qN|tW{+#~%HMý^MKtyyc%930(1a\ޮoFFJ4w gS2n;lDEFP=>gؼ2G=9)[iP8.^Ԧ!"""E9kڍUҮŮ~  yrO{>PP@cT0b*SXDDDDDDDDDDEDDDDDDDDD """"""""""`,""""""""""T0@c)*H1EDDDD*0p?~9˹FDDDDu"""""2M { aQu81ɧsr FmV 3~ݛ3pUq"""""RXDDD"dV|{~]j%4$ 4k{`90|1o2*\06M+̣({]mVBBPGUr/Jc᭏?cy%4TXDDDDTT0HQ'6r>ˊ+ \}n\Xr S=&P68r~Oq%MlY7 ?~Z[Ѭ]*HE`,"""r #.6(j Yw۰tZo#UcvU4m|h wn%/ jW#!6Lv4o܀MZcZk^ϦpA֭O`t-{^{T """"rR@@&\.'`e߲N;du̙;aw^۹N-"sڶlCplٵo/&>.и:Vcت AYy;}%_y%#1iۤ5LBc6Im ZE ڴl{ߌ!/+kUѶQ`mo`ΆټxoՕRZY!""""Rqs{n#,YVbbkWέ[P3)tJVdqIJEͰX4JkD||<[0ۍ/O+ش1_;UkWip`DFFa 9 Dvv6MgdՊioW!$IN$â:rE4 9%6-WYF"""""R1XDDD"a6Hz \Y.->Â0#LCB(tp9?$00Bg!eW>q5j~j1ž+EeĚusSgDcA)@DDQFE{5$rB$V޽眐7H;s5L6/fMA6gТ^~̼OSw捓}Ho {7YƆYq~ƞ9D9a \us\3f--UXU=6`* !h.2ۉo}cvzUyx;52&Hhݫ, ]RkQH=r)Cv}ks]籱cH>'o$m_1?;>{b1/.YW>e7^/eϗ35i&^w_0@ #kcjjwUzMHQXDDDkڤA,#:` HInk6ɜ\}PT'"$cth~LҺ),Bm.Ý1 KWqprzv3(ū2glj_Iq4_󉋎"6%{sD^=)s|p!KKwl=f"6Lr-͢kL];7ڲY`ɟ#HбDƧ60]ELJ >;{?i ;+֮[Z$ =WX8Fv#d=Ͽ&[ U3ϊee'soKLrsVβM9q?dדwʛ|c0 8S{⚁]{("&2#%u?^\7eQB䐞q."m/jXqN#ï:$ Wn`obn} -'*I)|Ηo-CB z2/%E5ֶu[{w1e˞ ٰi#/6}t!oJ1U[y}sLoLkx#"""C@YuW'=)#0kxY0HMi`n5˫W[0{g$V,.80H"#NIYFHDC#()͛ӫS{2w>-vNBb ЮuK8t@EG@Zj#ha2% t 8x`oVlAu;fxo +;~}G~OgNk/Ыn݊p6mBiIqdegӫ{{\v6{9C]NvQ ٰne+oMWkhӶ5#gdleBsLQ7Ҿg?Zdǻ`{Ŗ ejUCHMI%%%న*[6o|4sޒBEn>l ޙ9Ic[]^^^FPH"qÆ cRL!Cwqi&twİ#%su#(w8kf diKmK,9kKV+ eӆe,]l؈ǍKܦ^fd@ӦM7͆~:3 c ԡb UX%##u9 hզjWn-Oꘆ4o|#wf㦍!=fٶY~8=̙/xi]Yzpyxbjh"8$rl!j^YWunއ<g$ُUawӪUG-g?}[8hLt{,l|p3L wNqg5 G""""$u91<6ޙQоD%7hW:NApF[>Ԕ8#1OpE=  P^7͛ڵ3_}Sk6[EA[vqrUJDDY?t:, hvӬ/biՖf!&M5W͎g"Gm]۩9}&MC;j>8i-yx G+z1x IDAT=<6nQW[7e{_Go_m8^ÂkN阁1=NF܋?~)#{t_y1MN?. _߽;Y{v8hB\JsVAckRDDD"""" Ѿ% S3!EjogӖMtoQeEE5lgfsf RjcDF6prM.*,Oa8=pXetj o@eQ5 .]DEJj3&=13k/cqf4J`횥e#-Q jD?XA=7x!J۾QSBsMIh",ƙ l}øk[_'Ų3s]ޘ! EoUn5ﱿpCbdԄ'Ǎ9~`+;^#|X\ܽ'?͇OF tЃ[.ud1Wh$$0w·PDDD~zJ\ʧy;)qs6K" rv/llضc{hy)9X$%6/ 2o$idž-;I9v "VpAf~RL#Ȇ&B222DžV-QX6'^W9&\߿iWP-92^؂:i^ˋM)A4oC^q9 Hh͐GAP 1-1;v/7wڭBqQ\ӥ17"B\zH^Ց2Ң*q^|5>u@hPA?lÏP£cN&NHFFsa&7vMHLewz|^]La{I'93І)*[10{3lGq9|շƉ$w֫;׵ ;`s+}n} HNP}-:i8^kl> """J c eؔ(g$j;6†͏%SROtX(C!"!J:NRo,?(]2{h$`ҳs'qf%~v&1܎NVlxj4k[F&t~1V-eٺ-8lA|={vAXh) d]ԝ V^: `A#$^p:%мq">6fkEeYgU?w ?̲Lt+|~޿?E6}p6'} )Mf={|P>p屧m~G/|4{›\ީSYb6g3>Ʀ+ic3qxM;MW?,;NNYy8cN`1<~3jxܧ5wвٿaٽm#vzum˭ދ-8眘ffsp"B߫u7?0cHw|k l\=b&צR\,daobh6/uo<9x&LUVtF [e<x=g~ͬ1l% N6coN/=7߂=1/oJ?kNIX|_K3'<,iVaƼ%>imϻce^yyuiCƵW&Fm78^sb\yħv~&L|P'CFg:˧)g/Gu~6\v>Ķ3O?q6ȃL*V7FǘYqc!ϸW^䙿G_ce8pA;>{=!810p5L_ ʜ~g3m""J,X W>~sPc~X隑:Qo,4ztJx\^`5ܺ6G5Hh`l\KYI!a DSg߀lUK).* $(#C4ҳWh7~P3jbFIq& HLmɆM=DvӼu',?a(>}=A|nt!v2's>#7]0}ټ |[|3(z5&dݺض?x=?-5|@9_g}Se_W^tС[wjDIa>Qa ԇG܉f/.Y<%mawwh v >p{\ _u1-ڟOouwm#iZǘ(6|ccǐ}oo/>y+w 19Kugmn}}:,sD̛V[`s}k_ϾsƷy߭Mz=[W?ȉ XDDDWqm:Z6N&dqub"߻/L-Ͼ]ؘt56-g6 r(̝*DRz=W}G 4|G=^Ͼp8is( }Tv%{#aÜk‚؛z"}xi'|En?O=4t)q㧰dDuBxx/]SlϽE<2~E\y+OTw| hwrhWYq1<>o4hڅ?i^p"IÆ,\ټ0'I}xq)vFTF ۓS[[ug]3;O>s&sWؿ8;OO\˽x_s]uSFmHNΚ3OZa,"""VXRUJ\l,D8dƻޏ3*͛3[{2ݣ?kuKߦcV0Ms]2hwceeOht4g?08XP5_2%.ظ6l5L9$'rןaޢkЯ/C%2!J9rs٥gCci\ٷը|G l5i 'ru>zL:<6_̰7j쁑;}6<'&:Cv hPNAHh4G>Ki-+w䖰dt^;;{Xa}l˷|-7j[͛ ry߶tw U^P`wx7*P^s&7ݳnٲai7KMDқdylظrG͇D Zs AbEwmK߁bvݻdTil.<2!~(2Ztcys͒ 0fN;2 },){~~k>Y{>>:3ӢeK"}%kbB˃@l6]5wb@p1Ι3FsWpuW>pGv2WJۙ+_ou[bN_'_A-2~H:/v.+OdV|X HXkx1E}aN?j(`_$Ҡ]\yƇsOCڲǝ0k3杉oa/qpGu_}puu~(-ކWraN~`v{_.\Ō)ݳ ?;ťnܙ:7[sw +/'RRMns<-W~cw{No!)*7Y޲ZO>K?ɌB 0 /H5auku16>b}}Pum_.vO!-ZԼ{s}zj=\~Pݹ6L{D c_@l=J2u_ieXi`a ݓ*BwWH% ؓWW}AW`ఛ ?`Lx}:dӴMW°0MΫl We°9Ǽy;/zڇsxiStܓ'  33J~fSwGܹPf[:-fV'>0 ~H9`8^A6BBϣiD8n&!.(.V_Sl_8P/]粤o4`ʗ|$w搘 1W_G5_FEOU_^\~tMoܕ_ _y- \8W""vJ9^SvJZ:_ jiIMZ> s wqկ2 M̘1_)\Y.ðHM#5).Zui a? c^Ĕw9x`Weؗ2~3;oV݇C:__x9sgsw{zZҬYKV?R!b'hIhޙW&M!#v,ge72{Toozg7!I\6A?=ÏP£ge4Ȯ|/35y<}Z6ƯA:۶mN\)+VפdѮY&0v=Ĥ蜙NhN~(}/:  {RHiކrи5_r ON(,P~ի(rYL5uqxԆф4Ho1|7&+A>r@.nfii[[qZc8u+Fꔗ('U9߹u/3j_m`<\ѴߣED~m0 vpWl*lN86ÈmռڏmWeҧs;;Y sЬq#lx1ө-Kظe+ wUdłe_N'7Pt< A$=\۰]d./bʵ#A ~ՇZlͫoRthƟ;~q)u²|>o 7[WĂogjtfrvoYE̎,QVYRx`\u <{w Vqzu;Uɝ`sN#0Ovٶe {+[u]LDf26me)/9NLdCg[pN/-~_^: `A3><)sñIS g@v\oHcYvz40Ly 3i'*2؄0oF]J o6m K֬\~?;j֘;QMwϱO#O"2=ޱٹxS{\|0,_o]b4}}}XuhraĄ3m4~2hҤF}}kz=y $%MXUsk,_DDD~9]ٙ5vE8mÎpטgBDעY\|]ڒBD.h?[AÁReu]2 """""6[G ױk IDAT͂; w>Mw.QbZhK 17dĨ?`վȯAb@L%EDDDDyA?w""/s!"-)DDDDDDDDDDPXDDDDDDDDDD*(a,""""""""""""""""""""RA c0 JTPXDDDDDDDDDD%EDDDDDDDDD""""fY6V[PΙi:yI|j^T0_4Ӳr &WM;"""""?Y}H^}nJE̙ߛf?Sϲ/X? cc&aW #:Ч~,ї{""""k""""r "IG\>R6oJDA\b'P^ytɌgا4`EDDDI+EDDD^4NL;0mˡCBc=^wn}Z*^idvnDiq DңKW$`YvZC{STT ztJx\RE{/]Ǝ1]eaJbZKW#gZ\EGеSGbR8+Vw[E8}'is{dX Z m /CύcRآx,7Hn'i99Ɇ%9zv4OO~Obf; fŬi;H}>Zt:EDDDVHw gnW;s-=FӌXؘt56-g6 r(̝*`o~"

3ƶ w2x@.w )6Nߏ;3n̙ȽO@YHyeag5I 硇'8K /MM穧ڛn#+"""";0zbѪYs \w[{6rm!1:ИxJ`Ԟ bݣG)[YDHjJ#w2))[Vӣ}M[!11vmb <ƦѦ-[O^=Jb͚lFBB" HK{/1w$u#{Ef:Xlٰ^KoԐmҽKAvxصu5fxo +;~}W/mI!""""&.*$6l".9.=6qILa0IQNӤR!xvrx!)s$<4oT-0iذvnx{5lݻJy/ǎQ=0GQa>vVGVv""""RolY`&=J ٴ8AҸIKL d`$T96$(6 ,LNf<_@sØ%$'7f!&),Df݆i z<3ֳ͛qjZaiDvLD8=}{vbZ'{*똖{k?6^{yc0x!JFHJ?(82ss5j3XDDDDUE6,^^WVq 0hڬ5߯^i0]^HFF϶MZdu`x#*.uU٪ V/rǺ-;(KvK9왎fj<^v?ħsu<9~Q .^:--QXx.}_0 b t߷fU^G%sla0`ർ۔DJ60Vr(Ҙn ^͉8%EDDDYnɆKL#(2$IXdUW&Aƍed!aiѣ]+ްks؉ %42"1mѳ}6XzҥE3KM ,"Y2cia\ ]ڴfؘ;Nj\џG_&M%,ȟ=qq!6n/|[ts7_;L=w? :ͳ|]_@)PRU|ʿK2a,""""""""""z'""""""""""0@ cJH%EDDDDDDDDDPXDDDDDDDDDD*(a,""""""""""""""""""""RA c0 JTPXDDDD~ѼIff<;_8/l" &I a& z~06oW׶z؂P^Y?{mgςͼGHn_KfM|oMey7X5?t؈ 'UsnaQI4/""""K\ЊJa糈ӧ{W+(ib6\":<ú!+} _#6}Zf6ƿ<@g@u9uۯ\vw\geفc^>NNAQR1#GJ]f)l{CoDDDDG c+*/wA6L LJˎeo_Ι!}lY\=d axM%+װs&J h ] c 0Wq:O$j;tb.Vm- j-նm_1?;>{b =zĘ(+fy3aǤ,‚#DrdtX331֭ 絿αMmy """";\"BB0 ?6o ^ øzU\u\9Z"㈋ %,!`cڴ0dx3w6; 0MyyGHcT~:]DZ^WGH ;NĿb~yӉvc?V ٤Ć0l.""""0 ZT=fA+G9DEEƝJ1%@Il޴^ړA\\ {h=<#5w-#Gކ8|Oyg$oI6  +b_)V| |832 HPpU8 4/Wk\^?>""""r!і""""rjL7}n6nȊңmmxl\}т0gb4))-!c<0cڋø~Զɱi(*#$&~tjޟlq ϙ#~IpP(U.niY/f'KIigADDDD~0_àYz Xj#-GjJcΨ[2/i8' 0 {Su␠s̘iֹiU[P>ZQغ}GX,씔xa0k"A~G>]\NaBQaQ~‚HMIR\q=_|9[aDDDDWG c1Imªe uFƂ+ө˵liIMZԘ= hZM2WʢW˷*_GS_1J"4T$Ӣ`>cu% L(*>Dm[&2'""""˥\,`W DIU74?;8-$M4m^b[[DrAY@+ WEQ- `Y R֮B }MletRҖB :9=}Lrf>w~p$IRg`,IN- Sהb3sH$ȊE)Q qx1^C8d%0I~8z f͎h2N2wcKCF ev^>S9GsyCyn]ϨU\tR>0W=@k/oFN>??4j))Npd,om\$ ?̢[1;ط$IcI$e@g,e O3a!D~El|e>H,9^`9"+`u+aҖb/38O.gq)3EkY9o,]U0BSL`&_~7_3E@YptE.'NDWv\7^zy}>%W~J9{p-w/^Dni?~=\o0wђ>1>U7q)9ݘ~38aOn'H K{Q,]#h6,$I\X$IG\SsK2q(WWE/ @4/Nc^Z>;)//e)lM QZwۇw<55ѐH&IE^tޝ8oՠ;Dz{ٵG~5z GCqyVy*'r}fv{ 3sm\r'7F W~U^^6{~e%Eۏ}_~{4C ✏J~,{qq`O%,Tga;z,ɷ}%It9X$mDS[P'arbD94%ްfn* (D6ώPRVA[g0kR4l<I4V+"Ҕh#yɧ[Yɀw+!8yo=LQJv~Ѯ^ 'ϦPO"}`Qc;\'Lb㲇3۪5^MgGv5PH^=f<lٸXnC"D^dG2.&7k_o$IR'f`,I޶LN"$[[hNQSS8"ZiL4SSSC}}3pn|-QÇkѬ,jkVkڼnel5y޿^}iNˎ;wX8 ϶ZIeH4/S"B^X={K40vQ[x^mnaݖ&G&Ucȉ-=>D555$z vW}OXֵtׇFbC$ SL?7#IYs2z!;񋇼$I$i=.?/t hLo 7 gKp|x$I$eN$I$IK$I$I %I$I$I$I$I$)X$I$IK$I$I %I$I$I$I$I$)X$I$IK$I$I %I$I$I$I$I$)X$I$IK$I$I %I$I$I$I$I$)X$I$IK$I$I %I$I$ID3݀$I$IΡ7Qd2vH$LQ~fI$I$IR\4 ͣ/{"gs2#`>k8>UT׶qyiO` K$I$I:Ln'9ikfB\`#?|FIpN vD$I$I*ԭQF 0X-feԸm[od# Y%E񐉜uE24Pj:=0"M@Nr|ϙ?;zoO^HmoZ?! b8ocs_5_?SP1>]|9"a>Ot%%I$I$i 4AGĥkV R9~ZVIҫ][f|ʥѧ17Y Ɇf.ZΫjN9#ڟRYƕTƒ$I$I*Y1zPD>~R=~0m&.b3Kixyljݳgx~˼V" @Cf۹مf-Aj,[g2o ^yy%:ʊQH&nKD"QB s3 %I$I$F)).&#/;JnK Bdgg3$I$I$`,nݺe ekK$I$IcI$I$IR$I$I$ 00$I$I$K$I$IcI$I$IR$I$I$ 00$I$I$K$I$IcI$I$IR$I$I$ 00$I$I$K$I$IcI$I$IR$I$I$ 00$I$I$K$I$IcI$I$IRZt_/̚H!I$I$I:ZR-Ho2( d9@n9cwI$I$I! 4֔n58`gdg Dw$I$I$u~m =۸}q{_}q*= 1,Nc$I$I$iиW>8 Y$I$Ig[,86xoaq+>|K$I$I}{[3;4c`,I$I$I)vx;޾#2V$I$I㎷at܉p㎛$I$I$ R1g:[I$I$I;Cf:b9ˮUIENDB`gnome-firmware-3.34.0/data/000077500000000000000000000000001355437614600154475ustar00rootroot00000000000000gnome-firmware-3.34.0/data/appdata/000077500000000000000000000000001355437614600170615ustar00rootroot00000000000000gnome-firmware-3.34.0/data/appdata/meson.build000066400000000000000000000003711355437614600212240ustar00rootroot00000000000000i18n.merge_file( input: 'org.gnome.Firmware.metainfo.xml.in', output: 'org.gnome.Firmware.metainfo.xml', type: 'xml', po_dir: join_paths(meson.source_root(), 'po'), install: true, install_dir: join_paths(prefixed_datadir, 'metainfo'), ) gnome-firmware-3.34.0/data/appdata/org.gnome.Firmware.metainfo.xml.in000066400000000000000000000024761355437614600254700ustar00rootroot00000000000000 org.gnome.Firmware.desktop CC0-1.0 GPL-2.0+ Firmware

Install firmware on devices

This application can update, reinstall and downgrade firmware on devices supported by fwupd.

GNOME richard_at_hughsie.com gnome-firmware

This is the first stable release:

  • Show all device metadata supported by fwupd
  • Add ability to install, downgrade and reinstall firmware
  • Add ability to unlock hardware such as TPMs
  • Add ability to automatically enable the LVFS and refresh the metadata
gnome-firmware-3.34.0/data/icons/000077500000000000000000000000001355437614600165625ustar00rootroot00000000000000gnome-firmware-3.34.0/data/icons/meson.build000066400000000000000000000003271355437614600207260ustar00rootroot00000000000000install_data('org.gnome.Firmware.svg', install_dir : 'share/icons/hicolor/scalable/apps') install_data('org.gnome.Firmware-symbolic.svg', install_dir : 'share/icons/hicolor/symbolic/apps') gnome-firmware-3.34.0/data/icons/org.gnome.Firmware-symbolic.svg000066400000000000000000000173141355437614600245760ustar00rootroot00000000000000 image/svg+xml gnome-firmware-3.34.0/data/icons/org.gnome.Firmware.svg000066400000000000000000000546261355437614600227660ustar00rootroot00000000000000 Adwaita Icon Template image/svg+xml GNOME Design Team Adwaita Icon Template GNOME gnome-firmware-3.34.0/data/meson.build000066400000000000000000000004321355437614600176100ustar00rootroot00000000000000subdir('appdata') subdir('icons') i18n.merge_file( input: 'org.gnome.Firmware.desktop.in', output: 'org.gnome.Firmware.desktop', type: 'desktop', po_dir: join_paths(meson.source_root(), 'po'), install: true, install_dir: join_paths(prefixed_datadir, 'applications'), ) gnome-firmware-3.34.0/data/org.gnome.Firmware.desktop.in000066400000000000000000000006021355437614600231130ustar00rootroot00000000000000[Desktop Entry] Name=Firmware Comment=Install firmware on devices # TRANSLATORS: Do NOT translate or transliterate this text (this is an icon file name)! Icon=org.gnome.Firmware Exec=gnome-firmware Terminal=false Type=Application Categories=GNOME;GTK;System; # TRANSLATORS: these are desktop keywords - remember the trailing ';' :) Keywords=Firmware; StartupNotify=true NoDisplay=false gnome-firmware-3.34.0/meson.build000066400000000000000000000067321355437614600167100ustar00rootroot00000000000000project('gnome-firmware', 'c', version : '3.34.0', default_options : ['warning_level=1'], meson_version : '>=0.46.0' ) conf = configuration_data() conf.set('PACKAGE_VERSION', meson.project_version()) # get suported warning flags test_args = [ '-Waggregate-return', '-Warray-bounds', '-Wcast-align', '-Wclobbered', '-Wdeclaration-after-statement', '-Wempty-body', '-Wextra', '-Wformat=2', '-Wformat-nonliteral', '-Wformat-security', '-Wformat-signedness', '-Wignored-qualifiers', '-Wimplicit-function-declaration', '-Winit-self', '-Winline', '-Wmissing-declarations', '-Wmissing-format-attribute', '-Wmissing-include-dirs', '-Wmissing-noreturn', '-Wmissing-parameter-type', '-Wmissing-prototypes', '-Wnested-externs', '-Wno-discarded-qualifiers', '-Wno-missing-field-initializers', '-Wno-strict-aliasing', '-Wno-suggest-attribute=format', '-Wno-unused-parameter', '-Wno-cast-function-type', '-Wold-style-definition', '-Woverride-init', '-Wpacked', '-Wpointer-arith', '-Wredundant-decls', '-Wreturn-type', '-Wshadow', '-Wsign-compare', '-Wstrict-aliasing', '-Wstrict-prototypes', '-Wswitch-default', '-Wtype-limits', '-Wundef', '-Wuninitialized', '-Wunused-but-set-variable', '-Wwrite-strings' ] cc = meson.get_compiler('c') foreach arg: test_args if cc.has_argument(arg) add_project_arguments(arg, language : 'c') endif endforeach # enable full RELRO where possible # FIXME: until https://github.com/mesonbuild/meson/issues/1140 is fixed test_link_args = [ '-Wl,-z,relro', '-Wl,-z,now', ] foreach arg: test_link_args if cc.has_link_argument(arg) add_project_link_arguments(arg, language : 'c') endif endforeach prefixed_localedir = join_paths(get_option('prefix'), get_option('localedir')) prefixed_datadir = join_paths(get_option('prefix'), get_option('datadir')) prefixed_mandir = join_paths(get_option('prefix'), get_option('mandir')) libgtk = dependency('gtk+-3.0', version : '>= 3.11.2') libgio = dependency('gio-2.0') libfwupd = dependency('fwupd', version : '>= 1.2.10') libxmlb = dependency('xmlb', version : '>=0.1.7') libsoup = dependency('libsoup-2.4', version : '>= 2.51.92') gnome = import('gnome') i18n = import('i18n') conf.set_quoted('GETTEXT_PACKAGE', meson.project_name()) conf.set_quoted('VERSION', meson.project_version()) conf.set_quoted('LOCALEDIR', prefixed_localedir) if get_option('systemd') systemd = dependency('systemd', version : '>= 211') conf.set('HAVE_SYSTEMD' , '1') conf.set('HAVE_LOGIND' , '1') endif if get_option('elogind') elogind = dependency('libelogind', version : '>= 211') conf.set('HAVE_LOGIND' , '1') endif if get_option('consolekit') conf.set('HAVE_CONSOLEKIT' , '1') endif configure_file( output : 'config.h', configuration : conf, ) subdir('src') subdir('po') subdir('data') if meson.version().version_compare('<0.41.0') archiver = find_program('git', required : false) if archiver.found() run_target('dist', # git config tar.tar.xz.command "xz -c" command: [ 'git', 'archive', '--prefix=' + meson.project_name() + '-' + meson.project_version() + '/', 'HEAD', '--format=tar.xz', '--output', meson.project_name() + '-' + meson.project_version() + '.tar.xz' ] ) else message('git not found, you will not be able to run `ninja dist`') endif endif # FIXME: remove when https://github.com/mesonbuild/meson/issues/837 fixed meson.add_install_script('meson_post_install.sh') gnome-firmware-3.34.0/meson_options.txt000066400000000000000000000005451355437614600201770ustar00rootroot00000000000000option('consolekit', type : 'boolean', value : true, description : 'enable ConsoleKit support') option('systemd', type : 'boolean', value : true, description : 'enable systemd support') option('man', type : 'boolean', value : true, description : 'enable man pages') option('elogind', type : 'boolean', value : false, description : 'enable elogind support') gnome-firmware-3.34.0/meson_post_install.sh000077500000000000000000000003761355437614600210170ustar00rootroot00000000000000#!/bin/sh if [ -z $MESON_INSTALL_PREFIX ]; then echo 'This is meant to be ran from Meson only!' exit 1 fi if [ -z $DESTDIR ]; then echo 'Updating desktop database' update-desktop-database -q "$MESON_INSTALL_PREFIX/share/applications" fi gnome-firmware-3.34.0/po/000077500000000000000000000000001355437614600151545ustar00rootroot00000000000000gnome-firmware-3.34.0/po/.gitignore000066400000000000000000000000061355437614600171400ustar00rootroot00000000000000*.pot gnome-firmware-3.34.0/po/LINGUAS000066400000000000000000000000061355437614600161750ustar00rootroot00000000000000en_GB gnome-firmware-3.34.0/po/POTFILES.in000066400000000000000000000003311355437614600167260ustar00rootroot00000000000000# List of source files containing translatable strings. # Please keep this file sorted alphabetically. data/appdata/org.gnome.Firmware.metainfo.xml.in data/org.gnome.Firmware.desktop.in src/gfu-main.c src/gfu-main.ui gnome-firmware-3.34.0/po/en_GB.po000066400000000000000000000007341355437614600164720ustar00rootroot00000000000000# SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the fwupd package. # # Translators: # Richard Hughes , 2015,2017-2019 msgid "" msgstr "" "Project-Id-Version: gnome-firmware\n" "Report-Msgid-Bugs-To: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Language: en_GB\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" gnome-firmware-3.34.0/po/meson.build000066400000000000000000000001651355437614600173200ustar00rootroot00000000000000i18n.gettext(meson.project_name(), preset : 'glib', args: [ '--default-domain=' + meson.project_name() ] ) gnome-firmware-3.34.0/src/000077500000000000000000000000001355437614600153255ustar00rootroot00000000000000gnome-firmware-3.34.0/src/.gitignore000066400000000000000000000000061355437614600173110ustar00rootroot00000000000000*.ui~ gnome-firmware-3.34.0/src/gfu-common.c000066400000000000000000000327241355437614600175500ustar00rootroot00000000000000/* * Copyright (C) 2019 Andrew Schwenn * Copyright (C) 2019 Richard Hughes * * SPDX-License-Identifier: GPL-2.0+ */ #include "config.h" #include "gfu-common.h" /* formatting helper functions */ gchar * gfu_common_checksum_format (const gchar *checksum) { const gchar *checksum_type; guint len; /* guess type based on length */ if (checksum == NULL) return NULL; len = strlen (checksum); if (len == 32) checksum_type = "MD5"; else if (len == 40) checksum_type = "SHA1"; else if (len == 64) checksum_type = "SHA256"; else if (len == 128) checksum_type = "SHA512"; else checksum_type = "SHA1"; return g_strdup_printf ("%s(%s)", checksum_type, checksum); } const gchar * gfu_common_seconds_to_string (guint64 seconds) { guint64 minutes, hours; if (seconds == 0) return NULL; if (seconds >= 60) { minutes = seconds / 60; seconds = seconds % 60; if (minutes >= 60) { hours = minutes / 60; minutes = minutes % 60; return g_strdup_printf ("%lu hr, %lu min, %lu sec", hours, minutes, seconds); } return g_strdup_printf ("%lu min, %lu sec", minutes, seconds); } return g_strdup_printf ("%lu sec", seconds); } gchar * gfu_common_xml_to_text (const gchar *xml, GError **error) { g_autoptr(GString) str = g_string_new (NULL); g_autoptr(XbNode) n = NULL; g_autoptr(XbSilo) silo = NULL; if (xml == NULL) { g_set_error_literal (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "Null input XML"); return NULL; } /* parse XML */ silo = xb_silo_new_from_xml (xml, error); if (silo == NULL) return NULL; n = xb_silo_get_root (silo); while (n != NULL) { g_autoptr(XbNode) n2 = NULL; /* support

,

    ,
      and
    1. , ignore all else */ if (g_strcmp0 (xb_node_get_element (n), "p") == 0) { g_string_append_printf (str, "%s\n\n", xb_node_get_text (n)); } else if (g_strcmp0 (xb_node_get_element (n), "ul") == 0) { g_autoptr(GPtrArray) children = xb_node_get_children (n); for (guint i = 0; i < children->len; i++) { XbNode *nc = g_ptr_array_index (children, i); if (g_strcmp0 (xb_node_get_element (nc), "li") == 0) { g_string_append_printf (str, " • %s\n", xb_node_get_text (nc)); } } g_string_append (str, "\n"); } else if (g_strcmp0 (xb_node_get_element (n), "ol") == 0) { g_autoptr(GPtrArray) children = xb_node_get_children (n); for (guint i = 0; i < children->len; i++) { XbNode *nc = g_ptr_array_index (children, i); if (g_strcmp0 (xb_node_get_element (nc), "li") == 0) { g_string_append_printf (str, " %u. %s\n", i + 1, xb_node_get_text (nc)); } } g_string_append (str, "\n"); } n2 = xb_node_get_next (n); g_set_object (&n, n2); } /* remove extra newline */ if (str->len > 0) g_string_truncate (str, str->len - 1); /* success */ return g_string_free (g_steal_pointer (&str), FALSE); } const gchar * gfu_common_device_flags_to_strings (guint64 flags) { g_autoptr(GString) str = g_string_new (NULL); for (guint j = 0; j < 64; j++) { if ((flags & ((guint64) 1 << j)) == 0) continue; g_string_append_printf (str, "%s\n", fwupd_device_flag_to_string ((guint64) 1 << j)); } if (str->len == 0) { g_string_append (str, fwupd_device_flag_to_string (0)); } else { g_string_truncate (str, str->len - 1); } return g_string_free (g_steal_pointer (&str), FALSE); } const gchar * gfu_common_release_flags_to_strings (guint64 flags) { g_autoptr(GString) str = g_string_new (NULL); for (guint j = 0; j < 64; j++) { if ((flags & ((guint64) 1 << j)) == 0) continue; g_string_append_printf (str, "%s\n", fwupd_device_flag_to_string ((guint64) 1 << j)); } if (str->len == 0) { g_string_append (str, fwupd_release_flag_to_string (0)); } else { g_string_truncate (str, str->len - 1); } return g_string_free (g_steal_pointer (&str), FALSE); } /* handle needs-reboot and needs-shutdown */ gboolean gfu_common_system_shutdown (GError **error) { g_autoptr(GDBusConnection) connection = NULL; g_autoptr(GVariant) val = NULL; connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error); if (connection == NULL) return FALSE; #ifdef HAVE_LOGIND /* shutdown using logind */ val = g_dbus_connection_call_sync (connection, "org.freedesktop.login1", "/org/freedesktop/login1", "org.freedesktop.login1.Manager", "PowerOff", g_variant_new ("(b)", TRUE), NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, error); #elif defined(HAVE_CONSOLEKIT) /* shutdown using ConsoleKit */ val = g_dbus_connection_call_sync (connection, "org.freedesktop.ConsoleKit", "/org/freedesktop/ConsoleKit/Manager", "org.freedesktop.ConsoleKit.Manager", "Stop", NULL, NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, error); #else g_set_error_literal (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_ARGS, "No supported backend compiled in to perform the operation."); #endif return val != NULL; } gboolean gfu_common_system_reboot (GError **error) { g_autoptr(GDBusConnection) connection = NULL; g_autoptr(GVariant) val = NULL; connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error); if (connection == NULL) return FALSE; #ifdef HAVE_LOGIND /* reboot using logind */ val = g_dbus_connection_call_sync (connection, "org.freedesktop.login1", "/org/freedesktop/login1", "org.freedesktop.login1.Manager", "Reboot", g_variant_new ("(b)", TRUE), NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, error); #elif defined(HAVE_CONSOLEKIT) /* reboot using ConsoleKit */ val = g_dbus_connection_call_sync (connection, "org.freedesktop.ConsoleKit", "/org/freedesktop/ConsoleKit/Manager", "org.freedesktop.ConsoleKit.Manager", "Restart", NULL, NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, error); #else g_set_error_literal (error, FWUPD_ERROR, FWUPD_ERROR, "No supported backend compiled in to perform the operation."); #endif return val != NULL; } /* installation helper functions */ gboolean gfu_common_mkdir_parent (const gchar *filename, GError **error) { g_autofree gchar *parent = NULL; parent = g_path_get_dirname (filename); g_debug ("creating path %s", parent); if (g_mkdir_with_parents (parent, 0755) == -1) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "Failed to create '%s': %s", parent, g_strerror (errno)); return FALSE; } return TRUE; } gchar * gfu_get_user_cache_path (const gchar *fn) { g_autofree gchar *basename = g_path_get_basename (fn); return g_build_filename (g_get_user_cache_dir (), "gfu", basename, NULL); } gboolean gfu_common_file_exists_with_checksum (const gchar *fn, const gchar *checksum_expected, GChecksumType checksum_type) { gsize len = 0; g_autofree gchar *checksum_actual = NULL; g_autofree gchar *data = NULL; if (!g_file_get_contents (fn, &data, &len, NULL)) return FALSE; checksum_actual = g_compute_checksum_for_data (checksum_type, (guchar *) data, len); return g_strcmp0 (checksum_expected, checksum_actual) == 0; } SoupSession * gfu_common_setup_networking (GError **error) { const gchar *http_proxy; g_autofree gchar *user_agent = NULL; g_autoptr(SoupSession) session = NULL; /* create the soup session */ user_agent = fwupd_build_user_agent (GETTEXT_PACKAGE, VERSION); session = soup_session_new_with_options (SOUP_SESSION_USER_AGENT, user_agent, SOUP_SESSION_TIMEOUT, 60, NULL); if (session == NULL) { g_set_error_literal (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "failed to setup networking"); return NULL; } /* set the proxy */ http_proxy = g_getenv ("https_proxy"); if (http_proxy == NULL) http_proxy = g_getenv ("HTTPS_PROXY"); if (http_proxy == NULL) http_proxy = g_getenv ("http_proxy"); if (http_proxy == NULL) http_proxy = g_getenv ("HTTP_PROXY"); if (http_proxy != NULL && strlen (http_proxy) > 0) { g_autoptr(SoupURI) proxy_uri = soup_uri_new (http_proxy); if (proxy_uri == NULL) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "invalid proxy URI: %s", http_proxy); return NULL; } g_object_set (session, SOUP_SESSION_PROXY_URI, proxy_uri, NULL); } /* this disables the double-compression of the firmware.xml.gz file */ soup_session_remove_feature_by_type (session, SOUP_TYPE_CONTENT_DECODER); return g_steal_pointer (&session); } /* GTK helper functions */ gchar * gfu_common_device_flag_to_string (guint64 device_flag) { if (device_flag == FWUPD_DEVICE_FLAG_NONE) { return NULL; } if (device_flag == FWUPD_DEVICE_FLAG_INTERNAL) { /* TRANSLATORS: Device cannot be removed easily*/ return _("Internal device"); } if (device_flag == FWUPD_DEVICE_FLAG_UPDATABLE) { /* TRANSLATORS: Device is updatable in this or any other mode */ return _("Updatable"); } if (device_flag == FWUPD_DEVICE_FLAG_ONLY_OFFLINE) { /* TRANSLATORS: Update can only be done from offline mode */ return _("Update requires a reboot"); } if (device_flag == FWUPD_DEVICE_FLAG_REQUIRE_AC) { /* TRANSLATORS: Must be plugged in to an outlet */ return _("Requires AC power"); } if (device_flag == FWUPD_DEVICE_FLAG_LOCKED) { /* TRANSLATORS: Is locked and can be unlocked */ return _("Device is locked"); } if (device_flag == FWUPD_DEVICE_FLAG_SUPPORTED) { /* TRANSLATORS: Is found in current metadata */ return _("Supported on LVFS"); } if (device_flag == FWUPD_DEVICE_FLAG_NEEDS_BOOTLOADER) { /* TRANSLATORS: Requires a bootloader mode to be manually enabled by the user */ return _("Requires a bootloader"); } if (device_flag == FWUPD_DEVICE_FLAG_NEEDS_REBOOT) { /* TRANSLATORS: Requires a reboot to apply firmware or to reload hardware */ return _("Needs a reboot after installation"); } if (device_flag == FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN) { /* TRANSLATORS: Requires system shutdown to apply firmware */ return _("Needs shutdown after installation"); } if (device_flag == FWUPD_DEVICE_FLAG_REPORTED) { /* TRANSLATORS: Has been reported to a metadata server */ return _("Reported to LVFS"); } if (device_flag == FWUPD_DEVICE_FLAG_NOTIFIED) { /* TRANSLATORS: User has been notified */ return _("User has been notified"); } if (device_flag == FWUPD_DEVICE_FLAG_USE_RUNTIME_VERSION) { /* skip */ return NULL; } if (device_flag == FWUPD_DEVICE_FLAG_INSTALL_PARENT_FIRST) { /* TRANSLATORS: Install composite firmware on the parent before the child */ return _("Install to parent device first"); } if (device_flag == FWUPD_DEVICE_FLAG_IS_BOOTLOADER) { /* TRANSLATORS: Is currently in bootloader mode */ return _("Is in bootloader mode"); } if (device_flag == FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG) { /* TRANSLATORS: The hardware is waiting to be replugged */ return _("Hardware is waiting to be replugged"); } if (device_flag == FWUPD_DEVICE_FLAG_IGNORE_VALIDATION) { /* TRANSLATORS: Ignore validation safety checks when flashing this device */ return _("Ignore validation safety checks"); } if (device_flag == FWUPD_DEVICE_FLAG_ANOTHER_WRITE_REQUIRED) { /* skip */ return NULL; } if (device_flag == FWUPD_DEVICE_FLAG_NO_AUTO_INSTANCE_IDS) { /* skip */ return NULL; } if (device_flag == FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION) { /* TRANSLATORS: Device update needs to be separately activated */ return _("Device update needs activation"); } if (device_flag == FWUPD_DEVICE_FLAG_ENSURE_SEMVER) { /* skip */ return NULL; } if (device_flag == FWUPD_DEVICE_FLAG_UNKNOWN) { return NULL; } return NULL; } gchar * gfu_common_device_icon_from_flag (FwupdDeviceFlags device_flag) { if (device_flag == FWUPD_DEVICE_FLAG_INTERNAL) return "drive-harddisk-symbolic"; if (device_flag == FWUPD_DEVICE_FLAG_UPDATABLE) return "software-update-available-symbolic"; if (device_flag == FWUPD_DEVICE_FLAG_ONLY_OFFLINE) return "network-offline-symbolic"; if (device_flag == FWUPD_DEVICE_FLAG_REQUIRE_AC) return "battery-symbolic"; if (device_flag == FWUPD_DEVICE_FLAG_LOCKED) return "locked-symbolic"; if (device_flag == FWUPD_DEVICE_FLAG_SUPPORTED) return "security-high-symbolic"; if (device_flag == FWUPD_DEVICE_FLAG_NEEDS_BOOTLOADER) return "computer-symbolic"; if (device_flag == FWUPD_DEVICE_FLAG_NEEDS_REBOOT) return "system-reboot-symbolic"; if (device_flag == FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN) return "system-shutdown-symbolic"; if (device_flag == FWUPD_DEVICE_FLAG_REPORTED) return "task-due-symbolic"; if (device_flag == FWUPD_DEVICE_FLAG_NOTIFIED) return "task-due-symbolic"; if (device_flag == FWUPD_DEVICE_FLAG_USE_RUNTIME_VERSION) return "system-run-symbolic"; if (device_flag == FWUPD_DEVICE_FLAG_INSTALL_PARENT_FIRST) return "system-software-install-symbolic"; if (device_flag == FWUPD_DEVICE_FLAG_IS_BOOTLOADER) return "computer-symbolic"; if (device_flag == FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG) return "battery-low-symbolic"; if (device_flag == FWUPD_DEVICE_FLAG_IGNORE_VALIDATION) return "dialog-error-symbolic"; if (device_flag == FWUPD_DEVICE_FLAG_ANOTHER_WRITE_REQUIRED) return "media-floppy-symbolic"; if (device_flag == FWUPD_DEVICE_FLAG_NO_AUTO_INSTANCE_IDS) return "dialog-error-symbolic"; if (device_flag == FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION) return "emblem-important-symbolic"; if (device_flag == FWUPD_DEVICE_FLAG_ENSURE_SEMVER) return "emblem-important-symbolic"; if (device_flag == FWUPD_DEVICE_FLAG_UNKNOWN) return "unknown-symbolic"; return NULL; } gnome-firmware-3.34.0/src/gfu-common.h000066400000000000000000000030021355437614600175400ustar00rootroot00000000000000/* * Copyright (C) 2019 Andrew Schwenn * Copyright (C) 2019 Richard Hughes * * SPDX-License-Identifier: GPL-2.0+ */ #pragma once #include #include #include G_BEGIN_DECLS /* formatting helper functions */ gchar *gfu_common_checksum_format (const gchar *checksum); const gchar *gfu_common_seconds_to_string (guint64 seconds); gchar *gfu_common_xml_to_text (const gchar *xml, GError **error); const gchar *gfu_common_device_flags_to_strings (guint64 flags); const gchar *gfu_common_release_flags_to_strings (guint64 flags); /* handle needs-reboot and needs-shutdown */ gboolean gfu_common_system_shutdown (GError **error); gboolean gfu_common_system_reboot (GError **error); /* installation helper functions */ gboolean gfu_common_mkdir_parent (const gchar *filename, GError **error); gboolean gfu_common_file_exists_with_checksum (const gchar *fn, const gchar *checksum_expected, GChecksumType checksum_type); SoupSession *gfu_common_setup_networking (GError **error); gchar *gfu_get_user_cache_path (const gchar *fn); /* GTK helper functions */ gchar *gfu_common_device_flag_to_string (guint64 device_flag); gchar *gfu_common_device_icon_from_flag (FwupdDeviceFlags device_flag); G_END_DECLS gnome-firmware-3.34.0/src/gfu-device-row.c000066400000000000000000000075151355437614600203240ustar00rootroot00000000000000/* * Copyright (C) 2012-2019 Richard Hughes * * SPDX-License-Identifier: GPL-2.0+ */ #include "config.h" #include "gfu-device-row.h" typedef struct { FwupdDevice *device; GtkWidget *image; GtkWidget *name; GtkWidget *summary; guint pending_refresh_id; } GfuDeviceRowPrivate; G_DEFINE_TYPE_WITH_PRIVATE (GfuDeviceRow, gfu_device_row, GTK_TYPE_LIST_BOX_ROW) static void gfu_device_row_refresh (GfuDeviceRow *self) { const gchar *tmp; GPtrArray *icons; g_autoptr(GIcon) icon = g_themed_icon_new ("computer"); GfuDeviceRowPrivate *priv = gfu_device_row_get_instance_private (self); if (priv->device == NULL) return; /* row labels - name, summary */ tmp = fwupd_device_get_name (priv->device); gtk_label_set_label (GTK_LABEL (priv->name), tmp); tmp = fwupd_device_get_summary (priv->device); gtk_label_set_label (GTK_LABEL (priv->summary), tmp); gtk_widget_set_visible (priv->summary, TRUE); /* set icon, with fallbacks */ icons = fwupd_device_get_icons (priv->device); for (guint i = 0; i < icons->len; i++) { const gchar *icon_name = g_ptr_array_index (icons, i); g_themed_icon_prepend_name (G_THEMED_ICON (icon), icon_name); } gtk_image_set_from_gicon (GTK_IMAGE (priv->image), icon, -1); } FwupdDevice * gfu_device_row_get_device (GfuDeviceRow *self) { GfuDeviceRowPrivate *priv = gfu_device_row_get_instance_private (self); g_return_val_if_fail (GFU_IS_DEVICE_ROW (self), NULL); return priv->device; } static gboolean gfu_device_row_refresh_idle_cb (gpointer user_data) { GfuDeviceRow *self = GFU_DEVICE_ROW (user_data); GfuDeviceRowPrivate *priv = gfu_device_row_get_instance_private (self); priv->pending_refresh_id = 0; gfu_device_row_refresh (self); return FALSE; } static void gfu_device_row_notify_props_changed_cb (FwupdDevice *device, GParamSpec *pspec, GfuDeviceRow *self) { GfuDeviceRowPrivate *priv = gfu_device_row_get_instance_private (self); if (priv->pending_refresh_id > 0) return; priv->pending_refresh_id = g_idle_add (gfu_device_row_refresh_idle_cb, self); } static void gfu_device_row_set_device (GfuDeviceRow *self, FwupdDevice *device) { GfuDeviceRowPrivate *priv = gfu_device_row_get_instance_private (self); priv->device = g_object_ref (device); g_signal_connect_object (priv->device, "notify::state", G_CALLBACK (gfu_device_row_notify_props_changed_cb), self, 0); gfu_device_row_refresh (self); } static void gfu_device_row_destroy (GtkWidget *object) { GfuDeviceRow *self = GFU_DEVICE_ROW (object); GfuDeviceRowPrivate *priv = gfu_device_row_get_instance_private (self); if (priv->device != NULL) g_signal_handlers_disconnect_by_func (priv->device, gfu_device_row_notify_props_changed_cb, self); g_clear_object (&priv->device); if (priv->pending_refresh_id != 0) { g_source_remove (priv->pending_refresh_id); priv->pending_refresh_id = 0; } GTK_WIDGET_CLASS (gfu_device_row_parent_class)->destroy (object); } static void gfu_device_row_class_init (GfuDeviceRowClass *klass) { GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); widget_class->destroy = gfu_device_row_destroy; gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/Firmware/gfu-device-row.ui"); gtk_widget_class_bind_template_child_private (widget_class, GfuDeviceRow, image); gtk_widget_class_bind_template_child_private (widget_class, GfuDeviceRow, name); gtk_widget_class_bind_template_child_private (widget_class, GfuDeviceRow, summary); } static void gfu_device_row_init (GfuDeviceRow *self) { gtk_widget_set_has_window (GTK_WIDGET (self), FALSE); gtk_widget_init_template (GTK_WIDGET (self)); } GtkWidget * gfu_device_row_new (FwupdDevice *device) { GtkWidget *self; g_return_val_if_fail (FWUPD_IS_DEVICE (device), NULL); self = g_object_new (GFU_TYPE_DEVICE_ROW, NULL); gfu_device_row_set_device (GFU_DEVICE_ROW (self), device); return self; } gnome-firmware-3.34.0/src/gfu-device-row.h000066400000000000000000000010111355437614600203120ustar00rootroot00000000000000/* * Copyright (C) 2019 Richard Hughes * * SPDX-License-Identifier: GPL-2.0+ */ #pragma once #include #include G_BEGIN_DECLS #define GFU_TYPE_DEVICE_ROW (gfu_device_row_get_type ()) G_DECLARE_DERIVABLE_TYPE (GfuDeviceRow, gfu_device_row, GFU, DEVICE_ROW, GtkListBoxRow) struct _GfuDeviceRowClass { GtkListBoxRowClass parent_class; }; GtkWidget *gfu_device_row_new (FwupdDevice *device); FwupdDevice *gfu_device_row_get_device (GfuDeviceRow *self); G_END_DECLS gnome-firmware-3.34.0/src/gfu-device-row.ui000066400000000000000000000066601355437614600205170ustar00rootroot00000000000000 gnome-firmware-3.34.0/src/gfu-main.c000066400000000000000000001507701355437614600172060ustar00rootroot00000000000000/* * Copyright (C) 2019 Richard Hughes * * SPDX-License-Identifier: GPL-2.0+ */ #include "config.h" #include #include #include #include #include #include #include "gfu-device-row.h" #include "gfu-release-row.h" #include "gfu-common.c" /* gfu types */ typedef enum { GFU_MAIN_MODE_UNKNOWN, GFU_MAIN_MODE_DEVICE, GFU_MAIN_MODE_RELEASE, GFU_MAIN_MODE_LAST } GfuMainMode; typedef struct { GtkApplication *application; GtkBuilder *builder; GCancellable *cancellable; FwupdClient *client; FwupdDevice *device; FwupdRelease *release; GPtrArray *releases; GfuMainMode mode; GDBusProxy *proxy; SoupSession *soup_session; FwupdInstallFlags flags; } GfuMain; /* used to compare rows in a list */ typedef struct { GtkListBox *list; FwupdDevice *row; } GfuDeviceRowHelper; /* GTK helper functions */ static void gfu_main_container_remove_all_cb (GtkWidget *widget, gpointer user_data) { GtkContainer *container = GTK_CONTAINER (user_data); gtk_container_remove (container, widget); } static void gfu_main_container_remove_all (GtkContainer *container) { gtk_container_foreach (container, gfu_main_container_remove_all_cb, container); } static void gfu_main_error_dialog (GfuMain *self, const gchar *title, const gchar *message) { GtkWindow *window; GtkWidget *dialog; window = GTK_WINDOW (gtk_builder_get_object (self->builder, "dialog_main")); dialog = gtk_message_dialog_new (window, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "%s", title); if (message != NULL) { gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s", message); } gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); } static void gfu_main_activate_cb (GApplication *application, GfuMain *self) { GtkWindow *window; window = GTK_WINDOW (gtk_builder_get_object (self->builder, "dialog_main")); gtk_window_present (window); } static void gfu_main_set_label (GfuMain *self, const gchar *label_id, const gchar *text) { GtkWidget *w; g_autofree gchar *label_id_title = g_strdup_printf ("%s_title", label_id); /* hide empty box */ if (text == NULL) { w = GTK_WIDGET (gtk_builder_get_object (self->builder, label_id)); gtk_widget_set_visible (w, FALSE); w = GTK_WIDGET (gtk_builder_get_object (self->builder, label_id_title)); gtk_widget_set_visible (w, FALSE); return; } /* update and display */ w = GTK_WIDGET (gtk_builder_get_object (self->builder, label_id)); gtk_label_set_label (GTK_LABEL (w), text); gtk_widget_set_visible (w, TRUE); w = GTK_WIDGET (gtk_builder_get_object (self->builder, label_id_title)); gtk_widget_set_visible (w, TRUE); } static void gfu_main_set_label_title (GfuMain *self, const gchar *label_id, const gchar *text) { /* update only the title of a label */ GtkWidget *w; g_autofree gchar *label_id_title = g_strdup_printf ("%s_title", label_id); /* hide empty box */ if (text == NULL) { w = GTK_WIDGET (gtk_builder_get_object (self->builder, label_id)); gtk_widget_set_visible (w, FALSE); w = GTK_WIDGET (gtk_builder_get_object (self->builder, label_id_title)); gtk_widget_set_visible (w, FALSE); return; } /* update and display */ w = GTK_WIDGET (gtk_builder_get_object (self->builder, label_id_title)); gtk_label_set_label (GTK_LABEL (w), text); gtk_widget_set_visible (w, TRUE); w = GTK_WIDGET (gtk_builder_get_object (self->builder, label_id)); gtk_widget_set_visible (w, TRUE); } static void gfu_main_set_device_flags (GfuMain *self, guint64 flags) { GtkWidget *icon, *label; GtkGrid *w; gint count = 0; g_autoptr(GString) flag = g_string_new (NULL); w = GTK_GRID (gtk_builder_get_object (self->builder, "grid_device_flags")); /* clear the grid */ gtk_grid_remove_column (w, 0); gtk_grid_remove_column (w, 0); gtk_grid_insert_column (w, 0); gtk_grid_insert_column (w, 0); /* iterate through flags */ for (guint j = 0; j < 64; j++) { gchar *flag_tmp; /* if flag is not set, skip */ if ((flags & ((guint64) 1 << j)) == 0) continue; /* else, add a row for this flag */ g_string_set_size (flag, 0); flag_tmp = gfu_common_device_flag_to_string ((guint64) 1 << j); if (flag_tmp == NULL) continue; g_string_assign (flag, flag_tmp); gtk_grid_insert_row (w, count); icon = gtk_image_new_from_icon_name (gfu_common_device_icon_from_flag ((guint64) 1 << j), GTK_ICON_SIZE_BUTTON); gtk_widget_set_visible (icon, TRUE); gtk_grid_attach (w, icon, 0, count, 1, 1); label = gtk_label_new (flag->str); gtk_label_set_xalign (GTK_LABEL (label), 0); gtk_widget_set_visible (label, TRUE); gtk_grid_attach (w, label, 1, count, 1, 1); count++; } if (count == 0) { /* no flags were added */ gtk_widget_set_visible (GTK_WIDGET (w), FALSE); gtk_widget_set_visible (GTK_WIDGET (gtk_builder_get_object (self->builder, "label_device_flags_title")), FALSE); } else { gtk_widget_set_visible (GTK_WIDGET (w), TRUE); gtk_widget_set_visible (GTK_WIDGET (gtk_builder_get_object (self->builder, "label_device_flags_title")), TRUE); } } static void gfu_main_refresh_ui (GfuMain *self) { GtkWidget *w; g_autoptr(GPtrArray) upgrades = NULL; /* set stack */ w = GTK_WIDGET (gtk_builder_get_object (self->builder, "stack_main")); if (self->mode == GFU_MAIN_MODE_RELEASE) gtk_stack_set_visible_child_name (GTK_STACK (w), "firmware"); else if (self->mode == GFU_MAIN_MODE_DEVICE) gtk_stack_set_visible_child_name (GTK_STACK (w), "main"); else gtk_stack_set_visible_child_name (GTK_STACK (w), "loading"); /* set device information */ if (self->device != NULL) { GPtrArray *guids; g_autoptr(GString) attr = g_string_new (NULL); gchar *tmp; gchar *tmp2; gfu_main_set_label (self, "label_device_version", fwupd_device_get_version (self->device)); gfu_main_set_label (self, "label_device_version_lowest", fwupd_device_get_version_lowest (self->device)); gfu_main_set_label (self, "label_device_version_bootloader", fwupd_device_get_version_bootloader (self->device)); gfu_main_set_label (self, "label_device_update_error", fwupd_device_get_update_error (self->device)); gfu_main_set_label (self, "label_device_serial", fwupd_device_get_serial (self->device)); tmp = fwupd_device_get_vendor (self->device); tmp2 = fwupd_device_get_vendor_id (self->device); if (tmp != NULL && tmp2 != NULL) { g_autofree gchar *both = g_strdup_printf ("%s (%s)", tmp, tmp2); gfu_main_set_label (self, "label_device_vendor", both); } else if (tmp != NULL) { gfu_main_set_label (self, "label_device_vendor", tmp); } else if (tmp2 != NULL) { gfu_main_set_label (self, "label_device_vendor", tmp2); } else { gfu_main_set_label (self, "label_device_vendor", NULL); } g_string_append_printf (attr, "%u", fwupd_device_get_flashes_left (self->device)); gfu_main_set_label (self, "label_device_flashes_left", g_strcmp0 (attr->str, "0")? attr->str : NULL); gfu_main_set_label (self, "label_device_install_duration", gfu_common_seconds_to_string (fwupd_device_get_install_duration (self->device))); gfu_main_set_device_flags (self, fwupd_device_get_flags (self->device)); g_string_set_size (attr, 0); guids = fwupd_device_get_guids (self->device); /* extract GUIDs, append with newline */ for (guint i = 0; i < guids->len; i++) { g_string_append_printf (attr, "%s\n", (gchar *) g_ptr_array_index (guids, i)); } /* remove final newline, set label */ if (attr->len > 0) g_string_truncate (attr, attr->len - 1); gfu_main_set_label (self, "label_device_guids", attr->str); /* set GUIDs->GUID if only one */ gfu_main_set_label_title (self, "label_device_guids", ngettext ("GUID", "GUIDs", guids->len)); //fixme: get parents //FwupdDevice *parent = fwupd_device_get_parent (self->device); //g_print ("parent: %s\n", fwupd_device_get_name (parent)); } /* set release information */ if (self->release != NULL) { GPtrArray *cats = NULL; GPtrArray *checks = NULL; g_autofree gchar *desc = NULL; g_autoptr(GString) size = g_string_new (NULL); g_autoptr(GError) error = NULL; g_autoptr(GString) attr = g_string_new (NULL); gfu_main_set_label (self, "label_release_version", fwupd_release_get_version (self->release)); cats = fwupd_release_get_categories (self->release); if (cats->len == 0) { gfu_main_set_label (self, "label_release_categories", NULL); } else { for (guint i = 0; i < cats->len; i++) { g_string_append_printf (attr, "%s\n", (const gchar *) g_ptr_array_index (cats, i)); } gfu_main_set_label (self, "label_release_categories", attr->str); if (attr->len > 0) g_string_truncate (attr, attr->len - 1); if (cats->len == 1) { gfu_main_set_label_title (self, "label_release_categories", "Category"); } else { gfu_main_set_label_title (self, "label_release_categories", "Categories"); } } g_string_set_size (attr, 0); checks = fwupd_release_get_checksums (self->release); if (checks->len == 0) { gfu_main_set_label (self, "label_release_checksum", NULL); } else { for (guint i = 0; i < checks->len; i++) { g_autofree gchar *tmp = gfu_common_checksum_format (g_ptr_array_index (checks, i)); g_string_append_printf (attr, "%s\n", tmp); } if (attr->len > 0) g_string_truncate (attr, attr->len - 1); gfu_main_set_label (self, "label_release_checksum", attr->str); gfu_main_set_label_title (self, "label_release_checksum", ngettext ("Checksum", "Checksums", checks->len)); } gfu_main_set_label (self, "label_release_filename", fwupd_release_get_filename (self->release)); gfu_main_set_label (self, "label_release_protocol", fwupd_release_get_protocol (self->release)); gfu_main_set_label (self, "label_release_appstream_id", fwupd_release_get_appstream_id (self->release)); gfu_main_set_label (self, "label_release_remote_id", fwupd_release_get_remote_id (self->release)); gfu_main_set_label (self, "label_release_vendor", fwupd_release_get_vendor (self->release)); gfu_main_set_label (self, "label_release_summary", fwupd_release_get_summary (self->release)); g_string_set_size (attr, 0); desc = gfu_common_xml_to_text (fwupd_release_get_description (self->release), &error); if (desc == NULL) { g_debug ("failed to get release description for version %s: %s", fwupd_release_get_version (self->release), error->message); gfu_main_set_label (self, "label_release_description", NULL); } else { g_string_append (attr, desc); if (attr->len > 0) g_string_truncate (attr, attr->len - 1); gfu_main_set_label (self, "label_release_description", attr->str); } gfu_main_set_label (self, "label_release_size", g_format_size(fwupd_release_get_size (self->release))); gfu_main_set_label (self, "label_release_license", fwupd_release_get_license (self->release)); gfu_main_set_label (self, "label_release_flags", gfu_common_release_flags_to_strings (fwupd_release_get_flags (self->release))); gfu_main_set_label (self, "label_release_install_duration", gfu_common_seconds_to_string (fwupd_release_get_install_duration (self->release))); gfu_main_set_label (self, "label_release_update_message", fwupd_release_get_update_message (self->release)); /* install button */ w = GTK_WIDGET (gtk_builder_get_object (self->builder, "button_install")); if (self->device != NULL) { gtk_widget_set_sensitive (w, TRUE); if (self->release != NULL && self->releases != NULL) { if (fwupd_release_has_flag (self->release, FWUPD_RELEASE_FLAG_IS_UPGRADE)) { /* TRANSLATORS: upgrading the firmware */ gtk_button_set_label (GTK_BUTTON (w), _("Install Upgrade")); } else if (fwupd_release_has_flag (self->release, FWUPD_RELEASE_FLAG_IS_DOWNGRADE)) { /* TRANSLATORS: downgrading the firmware */ gtk_button_set_label (GTK_BUTTON (w), _("Install Downgrade")); } else { /* TRANSLATORS: installing the same firmware that is currently installed */ gtk_button_set_label (GTK_BUTTON (w), _("Reinstall")); } } } else { gtk_widget_set_sensitive (w, FALSE); /* TRANSLATORS: general install button in the event of an error; not clickable */ gtk_button_set_label (GTK_BUTTON (w), _("Install")); } } /* refresh button */ w = GTK_WIDGET (gtk_builder_get_object (self->builder, "menu_button")); gtk_widget_set_visible (w, self->mode != GFU_MAIN_MODE_RELEASE); /* back button */ w = GTK_WIDGET (gtk_builder_get_object (self->builder, "button_back")); gtk_widget_set_visible (w, self->mode == GFU_MAIN_MODE_RELEASE); /* unlock button */ w = GTK_WIDGET (gtk_builder_get_object (self->builder, "button_unlock")); gtk_widget_set_visible (w, self->device != NULL && fwupd_device_has_flag (self->device, FWUPD_DEVICE_FLAG_LOCKED)); /* verify button */ w = GTK_WIDGET (gtk_builder_get_object (self->builder, "button_verify")); gtk_widget_set_sensitive (w, self->device != NULL && !fwupd_device_has_flag (self->device, FWUPD_DEVICE_FLAG_LOCKED) && fwupd_device_has_flag (self->device, FWUPD_DEVICE_FLAG_SUPPORTED)); /* releases button */ w = GTK_WIDGET (gtk_builder_get_object (self->builder, "button_releases")); if (self->releases == NULL || self->releases->len == 0) { gtk_widget_set_sensitive (w, FALSE); } else { gtk_widget_set_sensitive (w, TRUE); } } static void gfu_main_remove_row (GtkWidget *row, GtkListBox *w) { gtk_container_remove (GTK_CONTAINER (w), row); } /* updating devices while the application is open */ static void gfu_main_device_added_cb (FwupdClient *client, FwupdDevice *device, GfuMain *self) { GtkWidget *w; GtkWidget *l; w = GTK_WIDGET (gtk_builder_get_object (self->builder, "listbox_main")); /* ignore if device can't be updated */ if (!fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE)) return; /* create and add new row for device */ l = gfu_device_row_new (device); gtk_widget_set_visible (l, TRUE); gtk_list_box_insert (GTK_LIST_BOX (w), l, -1); /* if no row is selected (list was previously empty), select the first one */ if (gtk_list_box_get_selected_row (GTK_LIST_BOX (w)) == NULL) gtk_list_box_select_row (GTK_LIST_BOX (w), GTK_LIST_BOX_ROW (l)); } static void gfu_main_remove_device_from_list (GtkWidget *row, GfuDeviceRowHelper *rowcheck) { FwupdDevice *result = rowcheck->row; GtkListBox *w = rowcheck->list; if (!FWUPD_IS_DEVICE (result)) return; if (fwupd_device_compare (gfu_device_row_get_device (GFU_DEVICE_ROW (row)), result) == 0) { /* if row to be removed is the currently selected row, select the first row of the list */ GtkListBoxRow *sel = gtk_list_box_get_selected_row (GTK_LIST_BOX (w)); if (fwupd_device_compare (gfu_device_row_get_device (GFU_DEVICE_ROW (sel)), result) == 0) { GtkListBoxRow *l = gtk_list_box_get_row_at_index (GTK_LIST_BOX (w), 0); if (l != NULL) gtk_list_box_select_row (GTK_LIST_BOX (w), l); } /* remove row corresponding to the device */ gtk_container_remove (GTK_CONTAINER (w), row); } } static void gfu_main_device_removed_cb (FwupdClient *client, FwupdDevice *device, GfuMain *self) { GtkContainer *w; g_autofree GfuDeviceRowHelper *rowcheck = g_new0 (GfuDeviceRowHelper, 1); w = GTK_CONTAINER (gtk_builder_get_object (self->builder, "listbox_main")); /* package data */ rowcheck->list = GTK_LIST_BOX (w); rowcheck->row = device; gtk_container_foreach (w, (GtkCallback) gfu_main_remove_device_from_list, rowcheck); } static void gfu_main_reboot_shutdown_prompt (GfuMain *self) { g_autoptr(GError) error = NULL; guint64 flags = fwupd_device_get_flags (self->device); /* if successful, prompt for reboot */ // FIXME: handle with device::changed instead of removing if (flags & FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN) { /* shutdown prompt */ GtkWindow *window; GtkWidget *dialog; window = GTK_WINDOW (gtk_builder_get_object (self->builder, "dialog_main")); dialog = gtk_message_dialog_new (window, GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, /* TRANSLATORS: prompting a shutdown to the user */ "%s", _("An update requires the system to shutdown to complete.")); gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "Shutdown now?"); switch (gtk_dialog_run (GTK_DIALOG (dialog))) { case GTK_RESPONSE_YES: if (!gfu_common_system_shutdown (&error)) { /* remove device from list until system is rebooted */ GtkContainer *w; g_autofree GfuDeviceRowHelper *rowcheck = g_new0 (GfuDeviceRowHelper, 1); w = GTK_CONTAINER (gtk_builder_get_object (self->builder, "listbox_main")); /* package data */ rowcheck->list = GTK_LIST_BOX (w); rowcheck->row = self->device; /* remove corresponding device */ gtk_container_foreach (w, (GtkCallback) gfu_main_remove_device_from_list, rowcheck); g_debug ("Failed to shutdown device: %s\n", error->message); /* TRANSLATORS: error in shutting down the system */ gfu_main_error_dialog (self, _("Failed to shutdown device"), _("A manual shutdown is required.")); } break; case GTK_RESPONSE_NO: break; default: break; } gtk_widget_destroy (dialog); } if (flags & FWUPD_DEVICE_FLAG_NEEDS_REBOOT) { /* shutdown prompt */ GtkWindow *window; GtkWidget *dialog; window = GTK_WINDOW (gtk_builder_get_object (self->builder, "dialog_main")); dialog = gtk_message_dialog_new (window, GTK_DIALOG_MODAL, GTK_MESSAGE_QUESTION, GTK_BUTTONS_YES_NO, /* TRANSLATORS: prompting a reboot to the user */ "%s", _("An update requires a reboot to complete.")); gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "Restart now?"); switch (gtk_dialog_run (GTK_DIALOG (dialog))) { case GTK_RESPONSE_YES: if (!gfu_common_system_reboot (&error)) { /* remove device from list until system is rebooted */ GtkContainer *w; g_autofree GfuDeviceRowHelper *rowcheck = g_new0 (GfuDeviceRowHelper, 1); w = GTK_CONTAINER (gtk_builder_get_object (self->builder, "listbox_main")); /* package data */ rowcheck->list = GTK_LIST_BOX (w); rowcheck->row = self->device; /* remove corresponding device */ gtk_container_foreach (w, (GtkCallback) gfu_main_remove_device_from_list, rowcheck); g_debug ("Failed to reboot device: %s\n", error->message); /* TRANSLATORS: error in rebooting down the system */ gfu_main_error_dialog (self, _("Failed to reboot device"), _("A manual reboot is required.")); } break; case GTK_RESPONSE_NO: break; default: break; } gtk_widget_destroy (dialog); } } #if 0 static void gfu_cancel_clicked_cb (GtkWidget *widget, GfuMain *self) { g_cancellable_cancel (self->cancellable); } #endif /* async getter functions */ static void gfu_main_update_remotes_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { GfuMain *self = (GfuMain *) user_data; GtkWidget *w; gboolean disabled_lvfs_remote = FALSE; gboolean enabled_any_download_remote = FALSE; g_autoptr(GError) error = NULL; g_autoptr(GVariant) val = g_dbus_proxy_call_finish (self->proxy, res, &error); g_autoptr(GPtrArray) remotes = NULL; if (val == NULL) { gfu_main_error_dialog (self, _("Failed to load list of remotes"), error->message); return; } remotes = fwupd_remote_array_from_variant (val); for (guint i = 0; i < remotes->len; i++) { FwupdRemote *remote = g_ptr_array_index (remotes, i); g_debug ("%s is %s", fwupd_remote_get_id (remote), fwupd_remote_get_enabled (remote) ? "enabled" : "disabled"); /* if another repository is turned on providing metadata */ if (fwupd_remote_get_enabled (remote)) { if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_DOWNLOAD) enabled_any_download_remote = TRUE; /* lvfs is present and disabled */ } else { if (g_strcmp0 (fwupd_remote_get_id (remote), "lvfs") == 0) disabled_lvfs_remote = TRUE; } } w = GTK_WIDGET (gtk_builder_get_object (self->builder, "infobar_enable_lvfs")); gtk_info_bar_set_revealed (GTK_INFO_BAR (w), disabled_lvfs_remote && !enabled_any_download_remote); } static void gfu_main_update_devices_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { GtkWidget *w; GtkWidget *l = NULL; GfuMain *self = (GfuMain *) user_data; g_autoptr(GError) error = NULL; g_autoptr(GPtrArray) devices = NULL; g_autoptr(GVariant) tmp = g_dbus_proxy_call_finish (self->proxy, res, &error); if (tmp == NULL) { gfu_main_error_dialog (self, _("Failed to load device list"), error->message); return; } devices = fwupd_device_array_from_variant (tmp); /* create a row in the listbox for each updatable device */ w = GTK_WIDGET (gtk_builder_get_object (self->builder, "listbox_main")); for (guint i = 0; i < devices->len; i++) { FwupdDevice *device = g_ptr_array_index (devices, i); /* skip devices that can't be updated */ if (!fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE) && !fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_LOCKED)) { g_debug ("ignoring non-updatable device: %s", fwupd_device_get_name (device)); continue; } l = gfu_device_row_new (device); gtk_widget_set_visible (l, TRUE); gtk_list_box_insert (GTK_LIST_BOX (w), l, -1); } /* if no row is selected and there are rows in the list, select the first one */ if (gtk_list_box_get_selected_row (GTK_LIST_BOX (w)) == NULL && l != NULL) { gtk_list_box_select_row (GTK_LIST_BOX (w), gtk_list_box_get_row_at_index (GTK_LIST_BOX (w), 0)); } } static void gfu_main_update_releases_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { GtkWidget *w; GfuMain *self = (GfuMain *) user_data; g_autoptr(GError) error = NULL; g_autoptr(GVariant) tmp = g_dbus_proxy_call_finish (self->proxy, res, &error); if (tmp == NULL) { /* No firmware found for this devices */ g_debug ("ignoring: %s", error->message); return; } self->releases = fwupd_release_array_from_variant (tmp); w = GTK_WIDGET (gtk_builder_get_object (self->builder, "listbox_firmware")); gfu_main_container_remove_all (GTK_CONTAINER (w)); for (guint i = 0; i < self->releases->len; i++) { FwupdRelease *release = g_ptr_array_index (self->releases, i); GtkWidget *l = gfu_release_row_new (release); gtk_widget_set_visible (l, TRUE); gtk_list_box_insert (GTK_LIST_BOX (w), l, -1); } gfu_main_refresh_ui (self); } /* installation code, some from fwupd-client */ static void gfu_main_set_install_loading_label (GfuMain *self, gchar *text) { GtkLabel *label = GTK_LABEL (gtk_builder_get_object (self->builder, "install_spinner_device_label")); gtk_label_set_label (label, text); label = GTK_LABEL (gtk_builder_get_object (self->builder, "install_spinner_release_label")); gtk_label_set_label (label, text); } static void gfu_main_show_install_loading (GfuMain *self, gboolean show) { GtkWidget *w = GTK_WIDGET (gtk_builder_get_object (self->builder, "dialog_main")); gtk_widget_set_sensitive (w, !show); w = GTK_WIDGET (gtk_builder_get_object (self->builder, "install_spinner_device")); gtk_widget_set_visible (w, show); w = GTK_WIDGET (gtk_builder_get_object (self->builder, "install_spinner_release")); gtk_widget_set_visible (w, show); w = GTK_WIDGET (gtk_builder_get_object (self->builder, "box_release_metadata")); gtk_widget_set_visible (w, !show); w = GTK_WIDGET (gtk_builder_get_object (self->builder, "box_device_metadata")); gtk_widget_set_visible (w, !show); } static void gfu_main_download_chunk_cb (SoupMessage *msg, SoupBuffer *chunk, gpointer user_data) { guint percentage; goffset header_size; goffset body_length; GfuMain *self = (GfuMain *) user_data; /* if it's returning "Found" or an error, ignore the percentage */ if (msg->status_code != SOUP_STATUS_OK) { g_debug ("ignoring status code %u (%s)", msg->status_code, msg->reason_phrase); return; } /* get data */ body_length = msg->response_body->length; header_size = soup_message_headers_get_content_length (msg->response_headers); /* size is not known */ if (header_size < body_length) return; /* calculate percentage */ percentage = (guint) ((100 * body_length) / header_size); g_debug ("progress: %u%%", percentage); gfu_main_set_install_loading_label (self, g_strdup_printf ("%s (%u%%)", _("Downloading"), percentage)); } static gboolean gfu_main_download_file (GfuMain *self, SoupURI *uri, const gchar *fn, const gchar *checksum_expected, GError **error) { GChecksumType checksum_type; guint status_code; g_autoptr(GError) error_local = NULL; g_autofree gchar *checksum_actual = NULL; g_autofree gchar *uri_str = NULL; g_autoptr(SoupMessage) msg = NULL; /* check if the file already exists with the right checksum */ checksum_type = fwupd_checksum_guess_kind (checksum_expected); if (gfu_common_file_exists_with_checksum (fn, checksum_expected, checksum_type)) { g_debug ("skipping download as file already exists"); gfu_main_set_install_loading_label (self, _("File already downloaded...")); return TRUE; } /* set up networking */ if (self->soup_session == NULL) { self->soup_session = gfu_common_setup_networking (error); if (self->soup_session == NULL) return FALSE; } /* download data */ uri_str = soup_uri_to_string (uri, FALSE); g_debug ("downloading %s to %s", uri_str, fn); gfu_main_set_install_loading_label (self, _("Downloading file...")); msg = soup_message_new_from_uri (SOUP_METHOD_GET, uri); if (msg == NULL) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "Failed to parse URI %s", uri_str); return FALSE; } if (g_str_has_suffix (uri_str, ".asc") || g_str_has_suffix (uri_str, ".p7b") || g_str_has_suffix (uri_str, ".p7c")) { /* TRANSLATORS: downloading new signing file */ g_debug ("%s %s\n", _("Fetching signature"), uri_str); gfu_main_set_install_loading_label (self, _("Fetching signature...")); } else if (g_str_has_suffix (uri_str, ".gz")) { /* TRANSLATORS: downloading new metadata file */ g_debug ("%s %s\n", _("Fetching metadata"), uri_str); gfu_main_set_install_loading_label (self, _("Fetching metadata...")); } else if (g_str_has_suffix (uri_str, ".cab")) { /* TRANSLATORS: downloading new firmware file */ g_debug ("%s %s\n", _("Fetching firmware"), uri_str); gfu_main_set_install_loading_label (self, _("Fetching firmware...")); } else { /* TRANSLATORS: downloading unknown file */ g_debug ("%s %s\n", _("Fetching file"), uri_str); gfu_main_set_install_loading_label (self, _("Fetching file...")); } g_signal_connect (msg, "got-chunk", G_CALLBACK (gfu_main_download_chunk_cb), self); status_code = soup_session_send_message (self->soup_session, msg); g_debug ("\n"); if (status_code == 429) { g_autofree gchar *str = g_strndup (msg->response_body->data, msg->response_body->length); if (g_strcmp0 (str, "Too Many Requests") == 0) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, /* TRANSLATORS: the server is rate-limiting downloads */ "%s", _("Failed to download due to server limit")); return FALSE; } g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, _("Failed to download due to server limit: %s"), str); return FALSE; } if (status_code != SOUP_STATUS_OK) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, _("Failed to download %s: %s"), uri_str, soup_status_get_phrase (status_code)); return FALSE; } /* verify checksum */ if (checksum_expected != NULL) { checksum_actual = g_compute_checksum_for_data (checksum_type, (guchar *) msg->response_body->data, (gsize) msg->response_body->length); if (g_strcmp0 (checksum_expected, checksum_actual) != 0) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, _("Checksum invalid, expected %s got %s"), checksum_expected, checksum_actual); return FALSE; } } /* save file */ if (!g_file_set_contents (fn, msg->response_body->data, msg->response_body->length, &error_local)) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_WRITE, _("Failed to save file: %s"), error_local->message); return FALSE; } return TRUE; } static gboolean gfu_main_download_metadata_for_remote (GfuMain *self, FwupdRemote *remote, GError **error) { g_autofree gchar *basename_asc = NULL; g_autofree gchar *basename_id_asc = NULL; g_autofree gchar *basename_id = NULL; g_autofree gchar *basename = NULL; g_autofree gchar *filename = NULL; g_autofree gchar *filename_asc = NULL; g_autoptr(SoupURI) uri = NULL; g_autoptr(SoupURI) uri_sig = NULL; /* generate some plausible local filenames */ basename = g_path_get_basename (fwupd_remote_get_filename_cache (remote)); basename_id = g_strdup_printf ("%s-%s", fwupd_remote_get_id (remote), basename); /* download the metadata */ filename = gfu_get_user_cache_path (basename_id); gfu_main_set_install_loading_label (self, _("Creating cache path...")); if (!gfu_common_mkdir_parent (filename, error)) return FALSE; uri = soup_uri_new (fwupd_remote_get_metadata_uri (remote)); gfu_main_set_install_loading_label (self, _("Preparing to download file...")); if (!gfu_main_download_file (self, uri, filename, NULL, error)) return FALSE; /* download the signature */ basename_asc = g_path_get_basename (fwupd_remote_get_filename_cache_sig (remote)); basename_id_asc = g_strdup_printf ("%s-%s", fwupd_remote_get_id (remote), basename_asc); filename_asc = gfu_get_user_cache_path (basename_id_asc); uri_sig = soup_uri_new (fwupd_remote_get_metadata_uri_sig (remote)); gfu_main_set_install_loading_label (self, _("Preparing to download file...")); if (!gfu_main_download_file (self, uri_sig, filename_asc, NULL, error)) return FALSE; /* send all this to fwupd */ return fwupd_client_update_metadata (self->client, fwupd_remote_get_id (remote), filename, filename_asc, NULL, error); } static void gfu_main_enable_lvfs_cb (GtkWidget *widget, GfuMain *self) { GtkWidget *w; g_autoptr(FwupdRemote) remote = NULL; g_autoptr(GError) error = NULL; /* hide notification */ w = GTK_WIDGET (gtk_builder_get_object (self->builder, "infobar_enable_lvfs")); gtk_info_bar_set_revealed (GTK_INFO_BAR (w), FALSE); /* enable remote */ gfu_main_show_install_loading (self, TRUE); if (!fwupd_client_modify_remote (self->client, "lvfs", "Enabled", "true", NULL, &error)) { gfu_main_error_dialog (self, _("Failed to enable LVFS"), error->message); gfu_main_show_install_loading (self, FALSE); return; } /* refresh the newly-enabled remote */ remote = fwupd_client_get_remote_by_id (self->client, "lvfs", NULL, &error); if (remote == NULL) { gfu_main_error_dialog (self, _("Failed to find LVFS"), error->message); gfu_main_show_install_loading (self, FALSE); return; } if (!gfu_main_download_metadata_for_remote (self, remote, &error)) { gfu_main_error_dialog (self, _("Failed to download metadata for LVFS"), error->message); gfu_main_show_install_loading (self, FALSE); return; } gfu_main_show_install_loading (self, FALSE); } static gboolean gfu_main_download_metadata (GfuMain *self, GError **error) { g_autoptr(GPtrArray) remotes = NULL; /* begin downloading, show loading animation */ gfu_main_show_install_loading (self, TRUE); remotes = fwupd_client_get_remotes (self->client, NULL, error); if (remotes == NULL) { gfu_main_show_install_loading (self, FALSE); return FALSE; } for (guint i = 0; i < remotes->len; i++) { FwupdRemote *remote = g_ptr_array_index (remotes, i); if (!fwupd_remote_get_enabled (remote)) continue; if (fwupd_remote_get_kind (remote) != FWUPD_REMOTE_KIND_DOWNLOAD) continue; if (!gfu_main_download_metadata_for_remote (self, remote, error)) { gfu_main_show_install_loading (self, FALSE); return FALSE; } } gfu_main_show_install_loading (self, FALSE); return TRUE; } static void gfu_main_activate_refresh_metadata (GSimpleAction *simple, GVariant *parameter, gpointer user_data) { GfuMain *self = (GfuMain *) user_data; g_autoptr(GError) error = NULL; if (!gfu_main_download_metadata (self, &error)) gfu_main_error_dialog (self, _("Failed to download metadata"), error->message); } static gboolean gfu_main_install_release_to_device (GfuMain *self, FwupdDevice *dev, FwupdRelease *rel, GError **error) { GPtrArray *checksums; const gchar *remote_id; const gchar *uri_tmp; g_autofree gchar *fn = NULL; g_autofree gchar *uri_str = NULL; g_autoptr(SoupURI) uri = NULL; /* work out what remote-specific URI fields this should use */ uri_tmp = fwupd_release_get_uri (rel); remote_id = fwupd_release_get_remote_id (rel); if (remote_id != NULL) { g_autoptr(FwupdRemote) remote = NULL; remote = fwupd_client_get_remote_by_id (self->client, remote_id, NULL, error); if (remote == NULL) return FALSE; /* local and directory remotes have the firmware already */ if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_LOCAL) { const gchar *fn_cache = fwupd_remote_get_filename_cache (remote); g_autofree gchar *path = g_path_get_dirname (fn_cache); fn = g_build_filename (path, uri_tmp, NULL); } else if (fwupd_remote_get_kind (remote) == FWUPD_REMOTE_KIND_DIRECTORY) { fn = g_strdup (uri_tmp + 7); } /* install with flags chosen by the user */ if (fn != NULL) { return fwupd_client_install (self->client, fwupd_device_get_id (dev), fn, self->flags, NULL, error); } uri_str = fwupd_remote_build_firmware_uri (remote, uri_tmp, error); if (uri_str == NULL) return FALSE; } else { uri_str = g_strdup (uri_tmp); } /* download file */ g_debug ("Downloading %s for %s...\n", fwupd_release_get_version (rel), fwupd_device_get_name (dev)); gfu_main_set_install_loading_label (self, _("Preparing to download file...")); /* place in gfu cache directory */ fn = gfu_get_user_cache_path (uri_str); /* TRANSLATORS: creating directory for the firmware download */ gfu_main_set_install_loading_label (self, _("Creating cache path...")); if (!gfu_common_mkdir_parent (fn, error)) return FALSE; checksums = fwupd_release_get_checksums (rel); uri = soup_uri_new (uri_str); if (!gfu_main_download_file (self, uri, fn, fwupd_checksum_get_best (checksums), error)) return FALSE; /* if the device specifies ONLY_OFFLINE automatically set this flag */ if (fwupd_device_has_flag (dev, FWUPD_DEVICE_FLAG_ONLY_OFFLINE)) self->flags |= FWUPD_INSTALL_FLAG_OFFLINE; gfu_main_set_install_loading_label (self, _("Installing file to device...")); return fwupd_client_install (self->client, fwupd_device_get_id (dev), fn, self->flags, NULL, error); } /* used to retrieve the current device post-install */ typedef struct { GfuMain *self; gchar *device_id; } GfuPostInstallHelper; static void gfu_main_update_devices_post_install_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { GtkContainer *w; GtkWidget *l = NULL; GPtrArray *devices; GfuPostInstallHelper *helper = (GfuPostInstallHelper*)user_data; g_autoptr(GError) error = NULL; g_autoptr(GVariant) tmp = g_dbus_proxy_call_finish (helper->self->proxy, res, &error); /* clear out device list */ w = GTK_CONTAINER (gtk_builder_get_object (helper->self->builder, "listbox_main")); gtk_list_box_unselect_all (GTK_LIST_BOX (w)); gtk_container_foreach (w, (GtkCallback) gfu_main_remove_row, w); if (tmp == NULL) { gfu_main_error_dialog (helper->self, _("Failed to load device list"), error->message); return; } devices = fwupd_device_array_from_variant (tmp); /* create a row in the listbox for each updatable device */ for (guint i = 0; i < devices->len; i++) { FwupdDevice *device = g_ptr_array_index (devices, i); /* skip devices that can't be updated */ if (!fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_UPDATABLE) && !fwupd_device_has_flag (device, FWUPD_DEVICE_FLAG_LOCKED)) { g_debug ("ignoring non-updatable device: %s", fwupd_device_get_name (device)); continue; } l = gfu_device_row_new (device); gtk_widget_set_visible (l, TRUE); gtk_list_box_insert (GTK_LIST_BOX (w), l, -1); /* update our current device now that new firmware has been installed */ if (g_strcmp0 (helper->device_id, fwupd_device_get_id (device)) == 0) { helper->self->device = device; gtk_list_box_select_row (GTK_LIST_BOX (w), GTK_LIST_BOX_ROW (l)); } } /* reboot or shutdown if necessary (UEFI update) */ gfu_main_reboot_shutdown_prompt (helper->self); /* if no row is selected and there are rows in the list, select the first one */ if (gtk_list_box_get_selected_row (GTK_LIST_BOX (w)) == NULL && l != NULL) { gtk_list_box_select_row (GTK_LIST_BOX (w), gtk_list_box_get_row_at_index (GTK_LIST_BOX (w), 0)); } /* update release list */ g_dbus_proxy_call (helper->self->proxy, "GetReleases", g_variant_new ("(s)", helper->device_id), G_DBUS_CALL_FLAGS_NONE, -1, helper->self->cancellable, (GAsyncReadyCallback) gfu_main_update_releases_cb, helper->self); } static void gfu_main_release_install_file_cb (GtkWidget *widget, GfuMain *self) { GtkWidget *window; GtkWidget *dialog; g_autoptr(GError) error = NULL; /* make sure user wants to install file to device */ window = GTK_WIDGET (gtk_builder_get_object (self->builder, "dialog_main")); dialog = gtk_message_dialog_new (GTK_WINDOW (window), GTK_DIALOG_USE_HEADER_BAR, GTK_MESSAGE_QUESTION, GTK_BUTTONS_NONE, _("%s firmware version %s"), fwupd_device_get_name (self->device), fwupd_release_get_version (self->release)); gtk_dialog_add_button (GTK_DIALOG (dialog), _("Cancel"), GTK_RESPONSE_CANCEL); if (!fwupd_release_has_flag (self->release, FWUPD_RELEASE_FLAG_IS_DOWNGRADE) && !fwupd_release_has_flag (self->release, FWUPD_RELEASE_FLAG_IS_UPGRADE)) { /* TRANSLATORS: installs the same firmware version which is currently on the device */ gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), _("This firmware version is already installed on the device. Reinstall this firmware file?")); gtk_dialog_add_button (GTK_DIALOG (dialog), _("Reinstall"), GTK_RESPONSE_OK); } else { gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), _("Install this firmware file?")); gtk_dialog_add_button (GTK_DIALOG (dialog), _("Install"), GTK_RESPONSE_OK); } /* handle dialog response */ switch (gtk_dialog_run (GTK_DIALOG (dialog))) { case GTK_RESPONSE_OK: { /* keep track of which device is being installed upon */ g_autoptr(GString) device_id = g_string_new (NULL); g_string_assign (device_id, fwupd_device_get_id (self->device)); gtk_widget_destroy (dialog); /* begin installing, show loading animation */ gfu_main_show_install_loading (self, TRUE); if (!gfu_main_install_release_to_device (self, self->device, self->release, &error)) { gfu_main_show_install_loading (self, FALSE); gfu_main_error_dialog (self, _("Failed to install firmware release"), error->message); } else { /* create helper struct */ GfuPostInstallHelper *helper = g_new0 (GfuPostInstallHelper, 1); guint64 flags = fwupd_device_get_flags (self->device); helper->self = self; helper->device_id = device_id->str; /* update device list */ g_dbus_proxy_call (self->proxy, "GetDevices", NULL, G_DBUS_CALL_FLAGS_NONE, -1, self->cancellable, (GAsyncReadyCallback) gfu_main_update_devices_post_install_cb, helper); gfu_main_show_install_loading (self, FALSE); g_debug ("Installation complete.\n"); // FIXME: device-changed signal if (flags & FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN || flags & FWUPD_DEVICE_FLAG_NEEDS_REBOOT) break; dialog = gtk_message_dialog_new (GTK_WINDOW (window), GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_OK, /* TRANSLATORS: inform the user that the installation was successful onto the device */ "%s", _("Installation successful")); gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), _("Installed firmware version %s on %s"), fwupd_release_get_version (self->release), fwupd_device_get_name (self->device)); gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); } break; } case GTK_RESPONSE_CANCEL: gtk_widget_destroy (dialog); gfu_main_show_install_loading (self, FALSE); break; default: gtk_widget_destroy (dialog); gfu_main_show_install_loading (self, FALSE); break; } } static void gfu_main_device_releases_cb (GtkWidget *widget, GfuMain *self) { self->mode = GFU_MAIN_MODE_RELEASE; gfu_main_refresh_ui (self); } static void gfu_main_button_back_cb (GtkWidget *widget, GfuMain *self) { self->mode = GFU_MAIN_MODE_DEVICE; gfu_main_refresh_ui (self); } static void gfu_main_device_verify_cb (GtkWidget *widget, GfuMain *self) { GtkWidget *dialog; GtkWidget *window = GTK_WIDGET (gtk_builder_get_object (self->builder, "dialog_main")); g_autoptr(GError) error = NULL; if (!fwupd_client_verify (self->client, fwupd_device_get_id (self->device), self->cancellable, &error)) { g_clear_error (&error); if (!fwupd_client_verify_update (self->client, fwupd_device_get_id (self->device), self->cancellable, &error)) { /* TRANSLATORS: verify means checking the actual checksum of the firmware */ gfu_main_error_dialog (self, _("Failed to verify device"), error->message); return; } else { if (!fwupd_client_verify (self->client, fwupd_device_get_id (self->device), self->cancellable, &error)) { gfu_main_error_dialog (self, _("Failed to verify device"), error->message); return; } } } dialog = gtk_message_dialog_new (GTK_WINDOW (window), GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, GTK_BUTTONS_OK, /* TRANSLATORS: inform the user that the firmware verification was successful */ "%s", _("Verification succeeded")); gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), /* TRANSLATORS: firmware is cryptographically identical */ _("%s firmware checksums matched"), fwupd_device_get_name (self->device)); gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); } static void gfu_main_update_title (GfuMain *self) { GtkWidget *w; w = GTK_WIDGET (gtk_builder_get_object (self->builder, "header")); gtk_header_bar_set_subtitle (GTK_HEADER_BAR (w), NULL); } static void gfu_main_about_activated_cb (GSimpleAction *action, GVariant *parameter, gpointer user_data) { GfuMain *self = (GfuMain *) user_data; GList *windows; GtkWindow *parent = NULL; const gchar *authors[] = { "Richard Hughes", "Andrew Schwenn", NULL }; const gchar *copyright = "Copyright \xc2\xa9 2019 Richard Hughes"; windows = gtk_application_get_windows (GTK_APPLICATION (self->application)); if (windows) parent = windows->data; gtk_show_about_dialog (parent, /* TRANSLATORS: the title of the about window */ "title", _("About GNOME Firmware"), /* TRANSLATORS: the application name for the about UI */ "program-name", _("GNOME Firmware"), "authors", authors, /* TRANSLATORS: one-line description for the app */ "comments", _("Install firmware on devices"), "copyright", copyright, "license-type", GTK_LICENSE_GPL_2_0, "logo-icon-name", "computer", /* TRANSLATORS: you can put your name here :) */ "translator-credits", _("translator-credits"), "version", VERSION, NULL); } static void gfu_main_quit_activated_cb (GSimpleAction *action, GVariant *parameter, gpointer user_data) { GfuMain *self = (GfuMain *) user_data; g_application_quit (G_APPLICATION (self->application)); } static GActionEntry actions[] = { { "about", gfu_main_about_activated_cb, NULL, NULL, NULL }, { "refresh", gfu_main_activate_refresh_metadata, NULL, NULL, NULL }, { "quit", gfu_main_quit_activated_cb, NULL, NULL, NULL } }; static void gfu_main_release_row_selected_cb (GtkListBox *box, GtkListBoxRow *row, GfuMain *self) { FwupdRelease *release; /* Ignore if not in the "release" view */ if (!GFU_IS_RELEASE_ROW (GFU_RELEASE_ROW (row))) return; release = gfu_release_row_get_release (GFU_RELEASE_ROW (row)); g_set_object (&self->release, release); gfu_main_refresh_ui (self); } static void gfu_main_device_row_selected_cb (GtkListBox *box, GtkListBoxRow *row, GfuMain *self) { FwupdDevice *device; g_autoptr(GError) error = NULL; if (row == NULL) return; device = gfu_device_row_get_device (GFU_DEVICE_ROW (row)); self->mode = GFU_MAIN_MODE_DEVICE; g_clear_pointer (&self->releases, g_ptr_array_unref); g_set_object (&self->device, device); /* async call for releases */ if (fwupd_device_has_flag (self->device, FWUPD_DEVICE_FLAG_UPDATABLE)) { g_dbus_proxy_call (self->proxy, "GetReleases", g_variant_new ("(s)", fwupd_device_get_id (self->device)), G_DBUS_CALL_FLAGS_NONE, -1, self->cancellable, (GAsyncReadyCallback) gfu_main_update_releases_cb, self); } gfu_main_refresh_ui (self); } static void gfu_main_signals_cb (GDBusProxy *proxy, const gchar *sender_name, const gchar *signal_name, GVariant *parameters, GfuMain *self) { g_autoptr(FwupdDevice) dev = NULL; if (g_strcmp0 (signal_name, "DeviceAdded") == 0) { dev = fwupd_device_from_variant (parameters); g_debug ("Emitting ::device-added(%s)", fwupd_device_get_id (dev)); gfu_main_device_added_cb (self->client, dev, self); return; } if (g_strcmp0 (signal_name, "DeviceRemoved") == 0) { dev = fwupd_device_from_variant (parameters); g_debug ("Emitting ::device-removed(%s)", fwupd_device_get_id (dev)); gfu_main_device_removed_cb (self->client, dev, self); return; } if (g_strcmp0 (signal_name, "DeviceChanged") == 0) { dev = fwupd_device_from_variant (parameters); g_debug ("Emitting ::device-changed(%s)", fwupd_device_get_id (dev)); // FIXME: handle device-changed return; } g_debug ("Unknown signal name '%s' from %s", signal_name, sender_name); } static void gfu_main_proxy_new_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { g_autoptr(GError) error = NULL; GfuMain *self = (GfuMain *) user_data; self->proxy = g_dbus_proxy_new_for_bus_finish (res, &error); if (self->proxy == NULL) { gfu_main_error_dialog (self, _("Error connecting to fwupd"), error->message); return; } /* async call for devices */ g_dbus_proxy_call (self->proxy, "GetDevices", NULL, G_DBUS_CALL_FLAGS_NONE, -1, self->cancellable, (GAsyncReadyCallback) gfu_main_update_devices_cb, self); /* async call for remotes */ g_dbus_proxy_call (self->proxy, "GetRemotes", NULL, G_DBUS_CALL_FLAGS_NONE, -1, self->cancellable, (GAsyncReadyCallback) gfu_main_update_remotes_cb, self); /* connecting signals for updating devices */ g_signal_connect (self->proxy, "g-signal", G_CALLBACK (gfu_main_signals_cb), self); } static void gfu_main_device_unlock_cb (GtkWidget *widget, GfuMain *self) { g_autoptr(GError) error = NULL; if (!fwupd_client_unlock (self->client, fwupd_device_get_id (self->device), self->cancellable, &error)) { gfu_main_error_dialog (self, _("Failed to unlock device"), error->message); } else { gfu_main_reboot_shutdown_prompt (self); } } static void gfu_main_infobar_close_cb (GtkInfoBar *infobar, gpointer user_data) { gtk_info_bar_set_revealed (infobar, FALSE); } static void gfu_main_infobar_response_cb (GtkInfoBar *infobar, gint response_id, gpointer user_data) { if (response_id == GTK_RESPONSE_CLOSE) gtk_info_bar_set_revealed (infobar, FALSE); } static void gfu_main_startup_cb (GApplication *application, GfuMain *self) { GtkWidget *w; GtkWidget *main_window; gint retval; g_autofree gchar *filename = NULL; g_autoptr(GError) error = NULL; g_autoptr(GPtrArray) devices = NULL; /* add application menu items */ g_action_map_add_action_entries (G_ACTION_MAP (application), actions, G_N_ELEMENTS (actions), self); /* get UI */ self->builder = gtk_builder_new (); retval = gtk_builder_add_from_resource (self->builder, "/org/gnome/Firmware/gfu-main.ui", &error); if (retval == 0) { g_warning ("failed to load ui: %s", error->message); return; } main_window = GTK_WIDGET (gtk_builder_get_object (self->builder, "dialog_main")); gtk_application_add_window (self->application, GTK_WINDOW (main_window)); /* hide window first so that the dialogue resizes itself without redrawing */ w = GTK_WIDGET (gtk_builder_get_object (self->builder, "stack_main")); gtk_stack_set_visible_child_name (GTK_STACK (w), "loading"); /* buttons */ w = GTK_WIDGET (gtk_builder_get_object (self->builder, "button_unlock")); g_signal_connect (w, "clicked", G_CALLBACK (gfu_main_device_unlock_cb), self); w = GTK_WIDGET (gtk_builder_get_object (self->builder, "button_verify")); g_signal_connect (w, "clicked", G_CALLBACK (gfu_main_device_verify_cb), self); w = GTK_WIDGET (gtk_builder_get_object (self->builder, "button_releases")); g_signal_connect (w, "clicked", G_CALLBACK (gfu_main_device_releases_cb), self); w = GTK_WIDGET (gtk_builder_get_object (self->builder, "button_back")); g_signal_connect (w, "clicked", G_CALLBACK (gfu_main_button_back_cb), self); w = GTK_WIDGET (gtk_builder_get_object (self->builder, "button_install")); g_signal_connect (w, "clicked", G_CALLBACK (gfu_main_release_install_file_cb), self); w = GTK_WIDGET (gtk_builder_get_object (self->builder, "button_infobar_enable_lvfs")); g_signal_connect (w, "clicked", G_CALLBACK (gfu_main_enable_lvfs_cb), self); w = GTK_WIDGET (gtk_builder_get_object (self->builder, "listbox_main")); g_signal_connect (w, "row-selected", G_CALLBACK (gfu_main_device_row_selected_cb), self); w = GTK_WIDGET (gtk_builder_get_object (self->builder, "listbox_firmware")); g_signal_connect (w, "row-selected", G_CALLBACK (gfu_main_release_row_selected_cb), self); w = GTK_WIDGET (gtk_builder_get_object (self->builder, "infobar_enable_lvfs")); g_signal_connect (w, "close", G_CALLBACK (gfu_main_infobar_close_cb), self); w = GTK_WIDGET (gtk_builder_get_object (self->builder, "infobar_enable_lvfs")); g_signal_connect (w, "response", G_CALLBACK (gfu_main_infobar_response_cb), self); /* show main UI */ gfu_main_update_title (self); gfu_main_refresh_ui (self); gtk_widget_show (main_window); g_dbus_proxy_new_for_bus (G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, NULL, FWUPD_DBUS_SERVICE, FWUPD_DBUS_PATH, FWUPD_DBUS_INTERFACE, self->cancellable, (GAsyncReadyCallback) gfu_main_proxy_new_cb, self); } static void gfu_main_free (GfuMain *self) { if (self->builder != NULL) g_object_unref (self->builder); if (self->cancellable != NULL) g_object_unref (self->cancellable); if (self->device != NULL) g_object_unref (self->device); if (self->release != NULL) g_object_unref (self->release); if (self->client != NULL) g_object_unref (self->client); if (self->application != NULL) g_object_unref (self->application); if (self->releases != NULL) g_ptr_array_unref (self->releases); if (self->proxy != NULL) g_object_unref (self->proxy); if (self->soup_session != NULL) g_object_unref (self->soup_session); g_free (self); } G_DEFINE_AUTOPTR_CLEANUP_FUNC(GfuMain, gfu_main_free) int main (int argc, char **argv) { gboolean verbose = FALSE; g_autoptr(GError) error = NULL; g_autoptr(GfuMain) self = g_new0 (GfuMain, 1); g_autoptr(GOptionContext) context = NULL; const GOptionEntry options[] = { { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, /* TRANSLATORS: command line option */ _("Show extra debugging information"), NULL }, { NULL} }; setlocale (LC_ALL, ""); bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR); bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); textdomain (GETTEXT_PACKAGE); gtk_init (&argc, &argv); /* TRANSLATORS: command description */ context = g_option_context_new (_("GNOME Firmware")); g_option_context_add_group (context, gtk_get_option_group (TRUE)); g_option_context_add_main_entries (context, options, NULL); if (!g_option_context_parse (context, &argc, &argv, &error)) { /* TRANSLATORS: the user has sausages for fingers */ g_print ("%s: %s\n", _("Failed to parse command line options"), error->message); return EXIT_FAILURE; } /* allow downgrades and reinstall */ self->flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER; self->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL; self->cancellable = g_cancellable_new (); self->client = fwupd_client_new (); /* ensure single instance */ self->application = gtk_application_new ("org.gnome.Firmware", 0); g_signal_connect (self->application, "startup", G_CALLBACK (gfu_main_startup_cb), self); g_signal_connect (self->application, "activate", G_CALLBACK (gfu_main_activate_cb), self); /* set verbose? */ if (verbose) g_setenv ("G_MESSAGES_DEBUG", "all", FALSE); /* wait */ return g_application_run (G_APPLICATION (self->application), argc, argv); } gnome-firmware-3.34.0/src/gfu-main.ui000066400000000000000000002311101355437614600173650ustar00rootroot00000000000000 1024 600 False computer True False Firmware True True True Go back True go-previous-symbolic 1 True True True primary_menu True open-menu-symbolic 1 end True False vertical True False vertical question True False 6 end Enable True True True True True 0 False False 0 False start 12 vertical 6 True False start LVFS is not enabled True True 0 True False start Firmware metadata can be obtained from the Linux Vendor Firmware Service at no cost. True True 1 True True 0 False False 0 True False True False 6 True True never in True False 0 True False True True 0 True False True False 21 vertical 12 True False center 9 12 True False end Current Version right 0 0 True False start 0.1.2 True 1 0 True False end Minimum Version 1 0 1 True False end Bootloader Version 1 0 2 True False start 0.1.2 True 1 1 True False start 0.1.2 True 1 2 True False end Vendor 1 0 3 True False start 0.1.2 True 1 3 True False end start GUIDs 1 0 4 True False start REPLACED True 1 4 True False end start Flags 1 0 5 True False 3 8 1 5 True False end Install Duration 1 0 6 True False start REPLACED True 1 6 True False end Flashes Left 1 0 7 True False start REPLACED True 1 7 True False end Update Error 1 0 8 True False start REPLACED True 1 8 True False end Serial Number 1 0 9 True False start REPLACED True 1 9 False False 0 True False end end start Unlock True True True True True 0 Verify True False True True True True 1 Show Releases True False True True True True 2 False True end 1 page0 page0 True False 21 vertical 12 64 64 True False center end True True True 0 True False center start Loading… True True 1 page1 page1 1 True True 1 main Main firmware True False True True in True False True False True True 0 True False True False 21 vertical 10 True True True False none True False center 12 9 12 True False end Version 0 0 True False start 0.1.2 True 1 0 True False end start Summary 0 1 True False start replaced True True 50 1 1 True False end start Description 0 2 True False start replaced True True 1 2 True False end Filename 0 3 True False start replaced True 1 3 True False end Protocol 0 4 True False start replaced True 1 4 True False end Remote ID 0 5 True False start replaced True 1 5 True False end AppStream ID 0 6 True False start replaced True 1 6 True False end start Checksum 0 7 True False start replaced True 1 7 True False end Vendor 0 8 True False start replaced True 1 8 True False end Size 0 9 True False start 0 True 1 9 True False end License 0 10 True False start replaced True 1 10 True False end start Flags 0 11 True False start replaced True 1 11 True False end Install Duration 0 12 True False start replaced True 1 12 True False end Update Message 0 13 True False start replaced True 1 13 True False end start Categories 0 14 True False start replaced True 1 14 True True 0 True False end start Install True False True True True True 0 False True end 1 page0 page0 True False 21 vertical True False True True True 0 150 True False vertical True False Loading... True True 0 False True 1 page1 page1 1 True True 1 firmware Firmware 1 True False center center vertical 9 32 32 True False True False False 0 True False Loading devices... False True 1 loading Loading 2 True False center center vertical 9 True False center end 64 face-sad-symbolic 6 False False 0 True False center start No devices found... False True 1 nodevices No Devices 3 True True 1
      _Refresh Metadata app.refresh _Install Firmware Archive app.install-file
      gnome-firmware-3.34.0/src/gfu-release-row.c000066400000000000000000000070411355437614600204770ustar00rootroot00000000000000/* * Copyright (C) 2012-2019 Richard Hughes * * SPDX-License-Identifier: GPL-2.0+ */ #include "config.h" #include "gfu-release-row.h" typedef struct { FwupdRelease *release; GtkWidget *image; GtkWidget *name; GtkWidget *version; guint pending_refresh_id; } GfuReleaseRowPrivate; G_DEFINE_TYPE_WITH_PRIVATE (GfuReleaseRow, gfu_release_row, GTK_TYPE_LIST_BOX_ROW) static void gfu_release_row_refresh (GfuReleaseRow *self) { const gchar *tmp; GfuReleaseRowPrivate *priv = gfu_release_row_get_instance_private (self); if (priv->release == NULL) return; tmp = fwupd_release_get_name (priv->release); gtk_label_set_label (GTK_LABEL (priv->name), tmp); tmp = fwupd_release_get_version (priv->release); gtk_label_set_label (GTK_LABEL (priv->version), tmp); gtk_widget_set_visible (priv->version, TRUE); /* TODO: set icon, e.g. security-high */ } FwupdRelease * gfu_release_row_get_release (GfuReleaseRow *self) { GfuReleaseRowPrivate *priv = gfu_release_row_get_instance_private (self); g_return_val_if_fail (GFU_IS_RELEASE_ROW (self), NULL); return priv->release; } static gboolean gfu_release_row_refresh_idle_cb (gpointer user_data) { GfuReleaseRow *self = GFU_RELEASE_ROW (user_data); GfuReleaseRowPrivate *priv = gfu_release_row_get_instance_private (self); priv->pending_refresh_id = 0; gfu_release_row_refresh (self); return FALSE; } static void gfu_release_row_notify_props_changed_cb (FwupdRelease *release, GParamSpec *pspec, GfuReleaseRow *self) { GfuReleaseRowPrivate *priv = gfu_release_row_get_instance_private (self); if (priv->pending_refresh_id > 0) return; priv->pending_refresh_id = g_idle_add (gfu_release_row_refresh_idle_cb, self); } static void gfu_release_row_set_release (GfuReleaseRow *self, FwupdRelease *release) { GfuReleaseRowPrivate *priv = gfu_release_row_get_instance_private (self); priv->release = g_object_ref (release); g_signal_connect_object (priv->release, "notify::state", G_CALLBACK (gfu_release_row_notify_props_changed_cb), self, 0); gfu_release_row_refresh (self); } static void gfu_release_row_destroy (GtkWidget *object) { GfuReleaseRow *self = GFU_RELEASE_ROW (object); GfuReleaseRowPrivate *priv = gfu_release_row_get_instance_private (self); if (priv->release != NULL) g_signal_handlers_disconnect_by_func (priv->release, gfu_release_row_notify_props_changed_cb, self); g_clear_object (&priv->release); if (priv->pending_refresh_id != 0) { g_source_remove (priv->pending_refresh_id); priv->pending_refresh_id = 0; } GTK_WIDGET_CLASS (gfu_release_row_parent_class)->destroy (object); } static void gfu_release_row_class_init (GfuReleaseRowClass *klass) { GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); widget_class->destroy = gfu_release_row_destroy; gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/Firmware/gfu-release-row.ui"); gtk_widget_class_bind_template_child_private (widget_class, GfuReleaseRow, image); gtk_widget_class_bind_template_child_private (widget_class, GfuReleaseRow, name); gtk_widget_class_bind_template_child_private (widget_class, GfuReleaseRow, version); } static void gfu_release_row_init (GfuReleaseRow *self) { gtk_widget_set_has_window (GTK_WIDGET (self), FALSE); gtk_widget_init_template (GTK_WIDGET (self)); } GtkWidget * gfu_release_row_new (FwupdRelease *release) { GtkWidget *self; g_return_val_if_fail (FWUPD_IS_RELEASE (release), NULL); self = g_object_new (GFU_TYPE_RELEASE_ROW, NULL); gfu_release_row_set_release (GFU_RELEASE_ROW (self), release); return self; } gnome-firmware-3.34.0/src/gfu-release-row.h000066400000000000000000000010261355437614600205010ustar00rootroot00000000000000/* * Copyright (C) 2019 Richard Hughes * * SPDX-License-Identifier: GPL-2.0+ */ #pragma once #include #include G_BEGIN_DECLS #define GFU_TYPE_RELEASE_ROW (gfu_release_row_get_type ()) G_DECLARE_DERIVABLE_TYPE (GfuReleaseRow, gfu_release_row, GFU, RELEASE_ROW, GtkListBoxRow) struct _GfuReleaseRowClass { GtkListBoxRowClass parent_class; }; GtkWidget *gfu_release_row_new (FwupdRelease *release); FwupdRelease *gfu_release_row_get_release (GfuReleaseRow *self); G_END_DECLS gnome-firmware-3.34.0/src/gfu-release-row.ui000066400000000000000000000066021355437614600206740ustar00rootroot00000000000000 gnome-firmware-3.34.0/src/gnome-firmware.gresource.xml000066400000000000000000000004161355437614600227640ustar00rootroot00000000000000 gfu-main.ui gfu-device-row.ui gfu-release-row.ui gnome-firmware-3.34.0/src/meson.build000066400000000000000000000017411355437614600174720ustar00rootroot00000000000000cargs = ['-DG_LOG_DOMAIN="Gfu"'] gnome_firmware_resources = gnome.compile_resources( 'gnome-firmware-resources', 'gnome-firmware.gresource.xml', source_dir : '.', c_name : 'gfu', ) gnome_firmware = executable( 'gnome-firmware', gnome_firmware_resources, sources : [ 'gfu-main.c', 'gfu-device-row.c', 'gfu-release-row.c', ], include_directories : [ include_directories('..'), ], dependencies : [ libgtk, libfwupd, libxmlb, libsoup, ], c_args : cargs, install : true, ) if get_option('man') help2man = find_program('help2man') custom_target('gnome-firmware-man', input : gnome_firmware, output : 'gnome-firmware.1', command : [ help2man, '@INPUT@', '--no-info', '--output', '@OUTPUT@', '--name', 'gnome-firmware', '--manual', 'User Commands', '--version-string', meson.project_version(), ], install : true, install_dir : join_paths(prefixed_mandir, 'man1'), ) endif